summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml1
-rwxr-xr-xcontrib/jenkins.sh25
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control29
-rw-r--r--debian/copyright23
-rw-r--r--debian/osmocom-bb.install0
-rwxr-xr-xdebian/rules7
-rw-r--r--debian/source/format1
-rw-r--r--doc/examples/mobile/default.cfg25
-rw-r--r--doc/examples/mobile/multi_ms.cfg26
-rw-r--r--doc/examples/modem/modem.cfg24
-rw-r--r--doc/manuals/Makefile1
-rw-r--r--include/l1ctl_proto.h185
-rw-r--r--include/l1gprs.h120
-rw-r--r--src/Makefile17
-rw-r--r--src/README.development2
-rwxr-xr-xsrc/host/fb_tools/bdf_to_c.py4
-rw-r--r--src/host/gprsdecode/Makefile.am3
-rw-r--r--src/host/gprsdecode/configure.ac2
-rw-r--r--src/host/gprsdecode/gprs.c4
-rw-r--r--src/host/gprsdecode/gsmtap.c6
-rw-r--r--src/host/gprsdecode/main.c4
-rw-r--r--src/host/gprsdecode/rlcmac.c4
-rw-r--r--src/host/gprsdecode/rlcmac.h7
-rw-r--r--src/host/gsmmap/.gitignore35
-rw-r--r--src/host/gsmmap/Makefile.am17
-rw-r--r--src/host/gsmmap/configure.ac26
-rw-r--r--src/host/gsmmap/geo.c47
-rwxr-xr-xsrc/host/gsmmap/git-version-gen151
-rw-r--r--src/host/layer23/.gitignore2
-rw-r--r--src/host/layer23/configure.ac51
-rw-r--r--src/host/layer23/include/osmocom/bb/Makefile.am2
-rw-r--r--src/host/layer23/include/osmocom/bb/common/Makefile.am25
-rw-r--r--src/host/layer23/include/osmocom/bb/common/apn.h82
-rw-r--r--src/host/layer23/include/osmocom/bb/common/apn_fsm.h29
-rw-r--r--src/host/layer23/include/osmocom/bb/common/gps.h4
-rw-r--r--src/host/layer23/include/osmocom/bb/common/l1ctl.h32
-rw-r--r--src/host/layer23/include/osmocom/bb/common/l1l2_interface.h4
-rw-r--r--src/host/layer23/include/osmocom/bb/common/l23_app.h67
-rw-r--r--src/host/layer23/include/osmocom/bb/common/logging.h10
-rw-r--r--src/host/layer23/include/osmocom/bb/common/ms.h116
-rw-r--r--src/host/layer23/include/osmocom/bb/common/networks.h23
-rw-r--r--src/host/layer23/include/osmocom/bb/common/osmocom_data.h144
-rw-r--r--src/host/layer23/include/osmocom/bb/common/settings.h348
-rw-r--r--src/host/layer23/include/osmocom/bb/common/sim.h66
-rw-r--r--src/host/layer23/include/osmocom/bb/common/subscriber.h (renamed from src/host/layer23/include/osmocom/bb/mobile/subscriber.h)104
-rw-r--r--src/host/layer23/include/osmocom/bb/common/support.h (renamed from src/host/layer23/include/osmocom/bb/mobile/support.h)10
-rw-r--r--src/host/layer23/include/osmocom/bb/common/sysinfo.h123
-rw-r--r--src/host/layer23/include/osmocom/bb/common/vty.h35
-rw-r--r--src/host/layer23/include/osmocom/bb/misc/Makefile.am9
-rw-r--r--src/host/layer23/include/osmocom/bb/misc/cell_log.h4
-rw-r--r--src/host/layer23/include/osmocom/bb/misc/geo.h (renamed from src/host/gsmmap/geo.h)0
-rw-r--r--src/host/layer23/include/osmocom/bb/misc/locate.h (renamed from src/host/gsmmap/locate.h)0
-rw-r--r--src/host/layer23/include/osmocom/bb/misc/log.h (renamed from src/host/gsmmap/log.h)6
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/Makefile.am6
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/app_mobile.h4
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gapk_io.h47
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm322.h32
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h43
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h96
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h45
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/mncc.h35
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h35
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/settings.h144
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/tch.h28
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/transaction.h24
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/voice.h7
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/vty.h14
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/Makefile.am10
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/gmm.h13
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/grr.h35
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/llc.h8
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/modem.h19
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/rlcmac.h8
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/sm.h8
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/sndcp.h10
-rw-r--r--src/host/layer23/include/osmocom/bb/modem/vty.h10
-rw-r--r--src/host/layer23/src/Makefile.am2
-rw-r--r--src/host/layer23/src/common/Makefile.am39
-rw-r--r--src/host/layer23/src/common/apn.c121
-rw-r--r--src/host/layer23/src/common/apn_fsm.c244
-rw-r--r--src/host/layer23/src/common/gps.c5
-rw-r--r--src/host/layer23/src/common/l1ctl.c313
-rw-r--r--src/host/layer23/src/common/l1ctl_lapdm_glue.c13
-rw-r--r--src/host/layer23/src/common/l1l2_interface.c8
-rw-r--r--src/host/layer23/src/common/logging.c96
-rw-r--r--src/host/layer23/src/common/main.c221
-rw-r--r--src/host/layer23/src/common/ms.c80
-rw-r--r--src/host/layer23/src/common/networks.c166
-rw-r--r--src/host/layer23/src/common/sap_fsm.c6
-rw-r--r--src/host/layer23/src/common/sap_interface.c9
-rw-r--r--src/host/layer23/src/common/sap_proto.c4
-rw-r--r--src/host/layer23/src/common/settings.c (renamed from src/host/layer23/src/mobile/settings.c)147
-rw-r--r--src/host/layer23/src/common/sim.c12
-rw-r--r--src/host/layer23/src/common/subscriber.c (renamed from src/host/layer23/src/mobile/subscriber.c)1193
-rw-r--r--src/host/layer23/src/common/support.c (renamed from src/host/layer23/src/mobile/support.c)25
-rw-r--r--src/host/layer23/src/common/sysinfo.c676
-rw-r--r--src/host/layer23/src/common/utils.c4
-rw-r--r--src/host/layer23/src/common/vty.c1499
-rw-r--r--src/host/layer23/src/misc/Makefile.am84
-rw-r--r--src/host/layer23/src/misc/app_bcch_scan.c36
-rw-r--r--src/host/layer23/src/misc/app_cbch_sniff.c65
-rw-r--r--src/host/layer23/src/misc/app_ccch_scan.c128
-rw-r--r--src/host/layer23/src/misc/app_cell_log.c61
-rw-r--r--src/host/layer23/src/misc/app_echo_test.c19
-rw-r--r--src/host/layer23/src/misc/bcch_scan.c4
-rw-r--r--src/host/layer23/src/misc/cell_log.c38
-rw-r--r--src/host/layer23/src/misc/geo.c3
-rw-r--r--src/host/layer23/src/misc/geo.h12
-rw-r--r--src/host/layer23/src/misc/gsmmap.c (renamed from src/host/gsmmap/gsmmap.c)66
-rw-r--r--src/host/layer23/src/misc/locate.c (renamed from src/host/gsmmap/locate.c)4
-rw-r--r--src/host/layer23/src/misc/log.c (renamed from src/host/gsmmap/log.c)18
-rw-r--r--src/host/layer23/src/misc/rslms.c13
-rw-r--r--src/host/layer23/src/mobile/Makefile.am66
-rw-r--r--src/host/layer23/src/mobile/app_mobile.c299
-rw-r--r--src/host/layer23/src/mobile/gapk_io.c547
-rw-r--r--src/host/layer23/src/mobile/gsm322.c710
-rw-r--r--src/host/layer23/src/mobile/gsm411_sms.c26
-rw-r--r--src/host/layer23/src/mobile/gsm414.c10
-rw-r--r--src/host/layer23/src/mobile/gsm44068_gcc_bcc.c1967
-rw-r--r--src/host/layer23/src/mobile/gsm480_ss.c108
-rw-r--r--src/host/layer23/src/mobile/gsm48_cc.c167
-rw-r--r--src/host/layer23/src/mobile/gsm48_mm.c1418
-rw-r--r--src/host/layer23/src/mobile/gsm48_rr.c2200
-rw-r--r--src/host/layer23/src/mobile/main.c173
-rw-r--r--src/host/layer23/src/mobile/mncc_sock.c20
-rw-r--r--src/host/layer23/src/mobile/mnccms.c637
-rw-r--r--src/host/layer23/src/mobile/primitives.c4
-rw-r--r--src/host/layer23/src/mobile/script_lua.c5
-rw-r--r--src/host/layer23/src/mobile/script_nolua.c4
-rw-r--r--src/host/layer23/src/mobile/tch.c234
-rw-r--r--src/host/layer23/src/mobile/tch_data.c587
-rw-r--r--src/host/layer23/src/mobile/tch_data_sock.c243
-rw-r--r--src/host/layer23/src/mobile/tch_voice.c155
-rw-r--r--src/host/layer23/src/mobile/transaction.c19
-rw-r--r--src/host/layer23/src/mobile/voice.c114
-rw-r--r--src/host/layer23/src/mobile/vty_interface.c2224
-rw-r--r--src/host/layer23/src/modem/Makefile.am41
-rw-r--r--src/host/layer23/src/modem/app_modem.c348
-rw-r--r--src/host/layer23/src/modem/gmm.c271
-rw-r--r--src/host/layer23/src/modem/grr.c875
-rw-r--r--src/host/layer23/src/modem/llc.c181
-rw-r--r--src/host/layer23/src/modem/rlcmac.c199
-rw-r--r--src/host/layer23/src/modem/sm.c277
-rw-r--r--src/host/layer23/src/modem/sndcp.c217
-rw-r--r--src/host/layer23/src/modem/vty.c450
-rw-r--r--src/host/osmocon/configure.ac5
-rw-r--r--src/host/osmocon/osmocon.c4
-rw-r--r--src/host/osmocon/osmoload.c4
-rw-r--r--src/host/osmocon/tpu_debug.c4
-rw-r--r--src/host/trxcon/.gitignore9
-rw-r--r--src/host/trxcon/Makefile.am57
-rw-r--r--src/host/trxcon/configure.ac38
-rw-r--r--src/host/trxcon/include/Makefile.am3
-rw-r--r--src/host/trxcon/include/osmocom/Makefile.am3
-rw-r--r--src/host/trxcon/include/osmocom/bb/Makefile.am9
l---------src/host/trxcon/include/osmocom/bb/l1ctl_proto.h1
l---------src/host/trxcon/include/osmocom/bb/l1gprs.h1
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am5
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h417
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/logging.h37
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/prim.h132
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am9
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h22
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h67
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/logging.h16
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/phyif.h120
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h61
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h64
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h169
-rw-r--r--src/host/trxcon/l1ctl.c916
-rw-r--r--src/host/trxcon/l1ctl.h26
-rw-r--r--src/host/trxcon/l1ctl_link.c316
-rw-r--r--src/host/trxcon/l1ctl_link.h48
l---------src/host/trxcon/l1ctl_proto.h1
-rw-r--r--src/host/trxcon/logging.h17
-rw-r--r--src/host/trxcon/m4/.gitkeep0
-rw-r--r--src/host/trxcon/sched_clck.c206
-rw-r--r--src/host/trxcon/sched_lchan_common.c232
-rw-r--r--src/host/trxcon/sched_lchan_pdtch.c191
-rw-r--r--src/host/trxcon/sched_lchan_rach.c180
-rw-r--r--src/host/trxcon/sched_lchan_tchf.c293
-rw-r--r--src/host/trxcon/sched_lchan_tchh.c500
-rw-r--r--src/host/trxcon/sched_lchan_xcch.c201
-rw-r--r--src/host/trxcon/sched_mframe.c2101
-rw-r--r--src/host/trxcon/sched_prim.c617
-rw-r--r--src/host/trxcon/sched_trx.c839
-rw-r--r--src/host/trxcon/sched_trx.h415
-rw-r--r--src/host/trxcon/scheduler.h38
-rw-r--r--src/host/trxcon/src/Makefile.am64
-rw-r--r--src/host/trxcon/src/l1ctl.c849
-rw-r--r--src/host/trxcon/src/l1ctl_server.c282
l---------src/host/trxcon/src/l1gprs.c1
-rw-r--r--src/host/trxcon/src/logging.c (renamed from src/host/trxcon/logging.c)41
-rw-r--r--src/host/trxcon/src/sched_lchan_common.c137
-rw-r--r--src/host/trxcon/src/sched_lchan_desc.c (renamed from src/host/trxcon/sched_lchan_desc.c)441
-rw-r--r--src/host/trxcon/src/sched_lchan_pdtch.c195
-rw-r--r--src/host/trxcon/src/sched_lchan_rach.c136
-rw-r--r--src/host/trxcon/src/sched_lchan_sch.c (renamed from src/host/trxcon/sched_lchan_sch.c)84
-rw-r--r--src/host/trxcon/src/sched_lchan_tchf.c441
-rw-r--r--src/host/trxcon/src/sched_lchan_tchh.c622
-rw-r--r--src/host/trxcon/src/sched_lchan_xcch.c181
-rw-r--r--src/host/trxcon/src/sched_mframe.c2102
-rw-r--r--src/host/trxcon/src/sched_prim.c410
-rw-r--r--src/host/trxcon/src/sched_trx.c894
-rw-r--r--src/host/trxcon/src/trx_if.c (renamed from src/host/trxcon/trx_if.c)421
-rw-r--r--src/host/trxcon/src/trxcon_fsm.c822
-rw-r--r--src/host/trxcon/src/trxcon_inst.c107
-rw-r--r--src/host/trxcon/src/trxcon_main.c (renamed from src/host/trxcon/trxcon.c)310
-rw-r--r--src/host/trxcon/src/trxcon_shim.c262
-rw-r--r--src/host/trxcon/trx_if.h83
-rw-r--r--src/host/trxcon/trxcon.h21
-rw-r--r--src/host/virt_phy/configure.ac27
-rw-r--r--src/host/virt_phy/include/Makefile.am13
-rw-r--r--src/host/virt_phy/include/osmocom/Makefile.am3
-rw-r--r--src/host/virt_phy/include/osmocom/bb/Makefile.am8
l---------src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h1
l---------src/host/virt_phy/include/osmocom/bb/l1gprs.h1
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am11
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/common_util.h (renamed from src/host/virt_phy/include/virtphy/common_util.h)0
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/gsmtapl1_if.h (renamed from src/host/virt_phy/include/virtphy/gsmtapl1_if.h)7
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h85
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sock.h (renamed from src/host/virt_phy/include/virtphy/l1ctl_sock.h)0
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/logging.h (renamed from src/host/virt_phy/include/virtphy/logging.h)3
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/osmo_mcast_sock.h (renamed from src/host/virt_phy/include/virtphy/osmo_mcast_sock.h)0
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h (renamed from src/host/virt_phy/include/virtphy/virt_l1_model.h)17
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_sched.h (renamed from src/host/virt_phy/include/virtphy/virt_l1_sched.h)6
-rw-r--r--src/host/virt_phy/include/osmocom/bb/virtphy/virtual_um.h (renamed from src/host/virt_phy/include/virtphy/virtual_um.h)0
-rw-r--r--src/host/virt_phy/include/virtphy/l1ctl_sap.h79
-rw-r--r--src/host/virt_phy/src/Makefile.am31
-rw-r--r--src/host/virt_phy/src/gsmtapl1_if.c130
-rw-r--r--src/host/virt_phy/src/l1ctl_sap.c183
-rw-r--r--src/host/virt_phy/src/l1ctl_sock.c4
l---------src/host/virt_phy/src/l1gprs.c1
-rw-r--r--src/host/virt_phy/src/logging.c119
-rw-r--r--src/host/virt_phy/src/shared/osmo_mcast_sock.c8
-rw-r--r--src/host/virt_phy/src/shared/virtual_um.c9
-rw-r--r--src/host/virt_phy/src/virt_l1_model.c7
-rw-r--r--src/host/virt_phy/src/virt_l1_sched_simple.c10
-rw-r--r--src/host/virt_phy/src/virt_prim_data.c20
-rw-r--r--src/host/virt_phy/src/virt_prim_fbsb.c13
-rw-r--r--src/host/virt_phy/src/virt_prim_pdch.c108
-rw-r--r--src/host/virt_phy/src/virt_prim_pm.c17
-rw-r--r--src/host/virt_phy/src/virt_prim_rach.c40
-rw-r--r--src/host/virt_phy/src/virt_prim_traffic.c20
-rw-r--r--src/host/virt_phy/src/virtphy.c34
-rw-r--r--src/shared/l1gprs.c831
-rw-r--r--src/shared/libosmocore/include/osmocom/core/bitvec.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/conv.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/crcgen.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/linuxrbtree.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/msgb.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/msgfile.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/serial.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/timer.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/timer_compat.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/core/write_queue.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/a5.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm0808.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h2
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/telnet_interface.h4
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vector.h4
-rw-r--r--src/shared/libosmocore/src/application.c4
-rw-r--r--src/shared/libosmocore/src/backtrace.c4
-rw-r--r--src/shared/libosmocore/src/bitvec.c4
-rw-r--r--src/shared/libosmocore/src/codec/gsm610.c4
-rw-r--r--src/shared/libosmocore/src/codec/gsm620.c4
-rw-r--r--src/shared/libosmocore/src/codec/gsm660.c4
-rw-r--r--src/shared/libosmocore/src/codec/gsm690.c4
-rw-r--r--src/shared/libosmocore/src/conv.c4
-rw-r--r--src/shared/libosmocore/src/crcXXgen.c.tpl4
-rw-r--r--src/shared/libosmocore/src/gsm/a5.c4
-rw-r--r--src/shared/libosmocore/src/gsm/auth_comp128v1.c4
-rw-r--r--src/shared/libosmocore/src/gsm/auth_core.c4
-rw-r--r--src/shared/libosmocore/src/gsm/auth_milenage.c4
-rw-r--r--src/shared/libosmocore/src/gsm/comp128.c4
-rw-r--r--src/shared/libosmocore/src/gsm/gprs_cipher_core.c4
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0480.c4
-rw-r--r--src/shared/libosmocore/src/gsm/gsm0808.c4
-rw-r--r--src/shared/libosmocore/src/gsm/gsm48.c4
-rw-r--r--src/shared/libosmocore/src/gsm/gsm48_ie.c4
-rw-r--r--src/shared/libosmocore/src/gsm/gsm_utils.c4
-rw-r--r--src/shared/libosmocore/src/gsm/lapd_core.c4
-rw-r--r--src/shared/libosmocore/src/gsm/lapdm.c4
-rw-r--r--src/shared/libosmocore/src/gsm/rsl.c4
-rw-r--r--src/shared/libosmocore/src/gsm/rxlev_stat.c4
-rw-r--r--src/shared/libosmocore/src/gsmtap_util.c4
-rw-r--r--src/shared/libosmocore/src/logging.c4
-rw-r--r--src/shared/libosmocore/src/logging_syslog.c4
-rw-r--r--src/shared/libosmocore/src/msgb.c4
-rw-r--r--src/shared/libosmocore/src/msgfile.c4
-rw-r--r--src/shared/libosmocore/src/panic.c4
-rw-r--r--src/shared/libosmocore/src/plugin.c4
-rw-r--r--src/shared/libosmocore/src/rate_ctr.c4
-rw-r--r--src/shared/libosmocore/src/rbtree.c4
-rw-r--r--src/shared/libosmocore/src/select.c4
-rw-r--r--src/shared/libosmocore/src/serial.c4
-rw-r--r--src/shared/libosmocore/src/signal.c4
-rw-r--r--src/shared/libosmocore/src/statistics.c4
-rw-r--r--src/shared/libosmocore/src/timer.c4
-rw-r--r--src/shared/libosmocore/src/vty/logging_vty.c4
-rw-r--r--src/shared/libosmocore/src/vty/telnet_interface.c4
-rw-r--r--src/shared/libosmocore/src/vty/utils.c4
-rw-r--r--src/shared/libosmocore/src/vty/vector.c4
-rw-r--r--src/shared/libosmocore/src/write_queue.c4
-rw-r--r--src/shared/libosmocore/tests/gsm0408/gsm0408_test.c4
-rw-r--r--src/shared/libosmocore/tests/gsm0808/gsm0808_test.c4
-rw-r--r--src/shared/libosmocore/tests/lapd/lapd_test.c4
-rw-r--r--src/shared/libosmocore/tests/msgfile/msgfile_test.c4
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.c4
-rw-r--r--src/shared/libosmocore/tests/smscb/smscb_test.c4
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.c4
-rw-r--r--src/shared/libosmocore/tests/ussd/ussd_test.c4
-rw-r--r--src/shared/libosmocore/utils/osmo-arfcn.c4
-rw-r--r--src/shared/libosmocore/utils/osmo-auc-gen.c4
-rwxr-xr-xsrc/shared/update-libosmocore.sh2
-rw-r--r--src/target/firmware/Makefile20
-rw-r--r--src/target/firmware/Makefile.inc8
-rw-r--r--src/target/firmware/abb/twl3025.c5
-rw-r--r--src/target/firmware/apps/compal_dsp_dump/main.c4
-rw-r--r--src/target/firmware/apps/hello_world/main.c6
-rw-r--r--src/target/firmware/apps/layer1/main.c7
-rw-r--r--src/target/firmware/apps/loader/main.c4
-rw-r--r--src/target/firmware/apps/loader_mtk/main.c4
-rw-r--r--src/target/firmware/apps/menu/main.c4
-rw-r--r--src/target/firmware/apps/rssi/main.c9
-rwxr-xr-xsrc/target/firmware/apps/simtest/main.c4
-rw-r--r--src/target/firmware/apps/snake_game/main.c4
-rw-r--r--src/target/firmware/battery/compal_e88.c5
-rw-r--r--src/target/firmware/board/common/readcal_tiffs.c4
-rw-r--r--src/target/firmware/board/common/tx_calchan.c4
-rw-r--r--src/target/firmware/board/compal/readcal_common.c4
-rw-r--r--src/target/firmware/board/compal/readcal_small.c4
-rw-r--r--src/target/firmware/board/compal/rf_tables.c4
-rw-r--r--src/target/firmware/board/compal_e86/init.c4
-rw-r--r--src/target/firmware/board/compal_e86/tx_ramps.c4
-rwxr-xr-xsrc/target/firmware/board/compal_e88/init.c4
-rw-r--r--src/target/firmware/board/compal_e88/tx_ramps.c4
-rw-r--r--src/target/firmware/board/compal_e99/init.c4
-rw-r--r--src/target/firmware/board/compal_e99/readcal.c4
-rw-r--r--src/target/firmware/board/fcdev3b/init.c4
-rw-r--r--src/target/firmware/board/gta0x/afcparams.c4
-rw-r--r--src/target/firmware/board/gta0x/init.c4
-rw-r--r--src/target/firmware/board/gta0x/rf_tables.c4
-rw-r--r--src/target/firmware/board/gtm900b/afcparams.c4
-rw-r--r--src/target/firmware/board/gtm900b/init.c4
-rw-r--r--src/target/firmware/board/mediatek/uart.c4
-rw-r--r--src/target/firmware/board/mt62xx/init.c4
-rw-r--r--src/target/firmware/board/pirelli_dpl10/init.c4
-rw-r--r--src/target/firmware/board/pirelli_dpl10/readcal.c4
-rw-r--r--src/target/firmware/board/pirelli_dpl10/rf_tables.c4
-rw-r--r--src/target/firmware/board/se_j100/init.c4
-rw-r--r--src/target/firmware/board/se_j100/tx_ramps.c4
-rw-r--r--src/target/firmware/board/se_k2x0/init.c140
-rw-r--r--src/target/firmware/board/se_k2x0/keymap.h28
-rw-r--r--src/target/firmware/board/se_k2x0/rffe_k2x0.c122
-rw-r--r--src/target/firmware/board/tr800/afcparams.c52
-rw-r--r--src/target/firmware/board/tr800/init.c257
-rw-r--r--src/target/firmware/board/tr800/keymap.h87
-rw-r--r--src/target/firmware/board/tr800/rffe_leo_quadband.c194
-rw-r--r--src/target/firmware/calypso/backlight.c4
-rw-r--r--src/target/firmware/calypso/buzzer.c4
-rw-r--r--src/target/firmware/calypso/clock.c4
-rw-r--r--src/target/firmware/calypso/dma.c4
-rw-r--r--src/target/firmware/calypso/dsp.c4
-rw-r--r--src/target/firmware/calypso/du.c4
-rw-r--r--src/target/firmware/calypso/i2c.c4
-rw-r--r--src/target/firmware/calypso/irq.c4
-rw-r--r--src/target/firmware/calypso/keypad.c4
-rw-r--r--src/target/firmware/calypso/rtc.c4
-rw-r--r--src/target/firmware/calypso/sim.c4
-rw-r--r--src/target/firmware/calypso/spi.c4
-rw-r--r--src/target/firmware/calypso/timer.c4
-rw-r--r--src/target/firmware/calypso/tpu.c4
-rw-r--r--src/target/firmware/calypso/tsp.c4
-rw-r--r--src/target/firmware/calypso/uart.c4
-rw-r--r--src/target/firmware/calypso/uwire.c4
-rw-r--r--src/target/firmware/comm/msgb.c4
-rw-r--r--src/target/firmware/comm/sercomm.c4
-rw-r--r--src/target/firmware/comm/sercomm_cons.c4
-rw-r--r--src/target/firmware/comm/timer.c4
-rw-r--r--src/target/firmware/fb/fb_bw8.c4
-rw-r--r--src/target/firmware/fb/fb_dummy.c4
-rw-r--r--src/target/firmware/fb/fb_k2x0.c190
-rw-r--r--src/target/firmware/fb/fb_rgb332.c4
-rw-r--r--src/target/firmware/fb/fb_s6b33b1x.c4
-rw-r--r--src/target/firmware/fb/fb_ssd1783.c4
-rw-r--r--src/target/firmware/fb/fb_ssd1963.c4
-rw-r--r--src/target/firmware/fb/fb_st7558.c4
-rw-r--r--src/target/firmware/fb/fb_td014.c4
-rw-r--r--src/target/firmware/fb/font.c4
-rw-r--r--src/target/firmware/fb/framebuffer.c4
-rw-r--r--src/target/firmware/flash/cfi_flash.c4
-rw-r--r--src/target/firmware/include/calypso/du.h4
-rw-r--r--src/target/firmware/include/calypso/l1_environment.h2
-rwxr-xr-xsrc/target/firmware/include/calypso/sim.h4
-rw-r--r--src/target/firmware/include/comm/timer.h4
-rw-r--r--src/target/firmware/include/layer1/async.h3
-rw-r--r--src/target/firmware/include/layer1/prim.h2
-rw-r--r--src/target/firmware/include/layer1/sync.h2
-rw-r--r--src/target/firmware/include/mtk/emi.h4
-rw-r--r--src/target/firmware/include/mtk/mt6235.h4
-rw-r--r--src/target/firmware/include/mtk/system.h4
-rw-r--r--src/target/firmware/layer1/afc.c4
-rw-r--r--src/target/firmware/layer1/agc.c4
-rw-r--r--src/target/firmware/layer1/async.c15
-rw-r--r--src/target/firmware/layer1/avg.c4
-rw-r--r--src/target/firmware/layer1/init.c4
-rw-r--r--src/target/firmware/layer1/l23_api.c75
-rw-r--r--src/target/firmware/layer1/mframe_sched.c4
-rw-r--r--src/target/firmware/layer1/prim_fbsb.c4
-rw-r--r--src/target/firmware/layer1/prim_freq.c4
-rw-r--r--src/target/firmware/layer1/prim_pm.c4
-rw-r--r--src/target/firmware/layer1/prim_rach.c30
-rw-r--r--src/target/firmware/layer1/prim_rx_nb.c6
-rw-r--r--src/target/firmware/layer1/prim_tch.c258
-rw-r--r--src/target/firmware/layer1/prim_tx_nb.c4
-rw-r--r--src/target/firmware/layer1/prim_utils.c4
-rw-r--r--src/target/firmware/layer1/rfch.c4
-rw-r--r--src/target/firmware/layer1/sched_gsmtime.c4
-rw-r--r--src/target/firmware/layer1/sync.c4
-rw-r--r--src/target/firmware/layer1/tdma_sched.c4
-rw-r--r--src/target/firmware/layer1/toa.c4
-rw-r--r--src/target/firmware/layer1/tpu_window.c4
-rw-r--r--src/target/firmware/lib/console.c4
-rw-r--r--src/target/firmware/rf/mt6139.c4
-rw-r--r--src/target/firmware/rf/trf6151.c4
-rwxr-xr-xsrc/target/firmware/solve_envs.py2
-rw-r--r--src/target/firmware/tiffs/init.c4
-rw-r--r--src/target/firmware/tiffs/readfile.c4
-rw-r--r--src/target/trx_toolkit/app_common.py4
-rw-r--r--src/target/trx_toolkit/burst_fwd.py6
-rwxr-xr-xsrc/target/trx_toolkit/burst_gen.py4
-rwxr-xr-xsrc/target/trx_toolkit/burst_send.py4
-rwxr-xr-xsrc/target/trx_toolkit/clck_gen.py4
-rw-r--r--src/target/trx_toolkit/codec.py4
-rwxr-xr-xsrc/target/trx_toolkit/ctrl_cmd.py4
-rw-r--r--src/target/trx_toolkit/ctrl_if.py11
-rw-r--r--src/target/trx_toolkit/ctrl_if_trx.py40
-rw-r--r--src/target/trx_toolkit/data_dump.py4
-rw-r--r--src/target/trx_toolkit/data_if.py4
-rw-r--r--src/target/trx_toolkit/data_msg.py4
-rw-r--r--src/target/trx_toolkit/fake_pm.py4
-rwxr-xr-xsrc/target/trx_toolkit/fake_trx.py36
-rw-r--r--src/target/trx_toolkit/gsm_shared.py4
-rw-r--r--src/target/trx_toolkit/rand_burst_gen.py4
-rw-r--r--src/target/trx_toolkit/test_codec.py4
-rw-r--r--src/target/trx_toolkit/test_data_dump.py4
-rw-r--r--src/target/trx_toolkit/test_data_msg.py4
-rw-r--r--src/target/trx_toolkit/transceiver.py74
-rw-r--r--src/target/trx_toolkit/trx_list.py4
-rwxr-xr-xsrc/target/trx_toolkit/trx_sniff.py4
-rw-r--r--src/target/trx_toolkit/trxd_proto.py4
-rw-r--r--src/target/trx_toolkit/udp_link.py4
-rw-r--r--src/target/ui-experiment/font.hbin61204 -> 61076 bytes
460 files changed, 30238 insertions, 14329 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..7592debf
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: osmocom
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index fafbdfba..31278737 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -17,14 +17,17 @@ osmo-clean-workspace.sh
mkdir "$deps" || true
-osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
-
-# TODO: ask whether fail is expected, because osmocom-bb build succeeds?
-#"$deps"/libosmocore/contrib/verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
+# exclude ancient local copy of libosmocore.git
+verify_value_string_arrays_are_terminated.py \
+ $(find . -path ./src/shared/libosmocore -prune -o -name '*.[hc]' -print)
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
+osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
+osmo-build-dep.sh libosmo-gprs
+osmo-build-dep.sh gapk
+
set +x
echo
echo
@@ -35,19 +38,21 @@ set -x
# building those sub-projects where 'distcheck' is known-working
-for dir in gprsdecode layer23; do
+for dir in gprsdecode layer23 trxcon virt_phy; do
cd $base/src/host/$dir
autoreconf -fi
- ./configure
- make distcheck
+ ./configure --enable-werror
+ $MAKE $PARALLEL_MAKE
+ DISTCHECK_CONFIGURE_FLAGS="--enable-werror" $MAKE $PARALLEL_MAKE distcheck
done
# TODO: make sure 'distcheck' passes also for these
-for dir in gsmmap osmocon trxcon virt_phy; do
+# TODO: make sure '--enable-werror' passes also for these
+for dir in osmocon; do
cd $base/src/host/$dir
autoreconf -fi
./configure
- make
+ $MAKE $PARALLEL_MAKE
done
# Build and publish manuals
@@ -61,7 +66,7 @@ if [ "$WITH_MANUALS" = "1" ]; then
fi
# Test 'maintainer-clean'
-for dir in gprsdecode layer23 gsmmap osmocon trxcon virt_phy; do
+for dir in gprsdecode layer23 osmocon trxcon virt_phy; do
cd "$base/src/host/$dir"
make maintainer-clean
done
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 00000000..2335d186
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+osmocom-bb (0.1.0) unstable; urgency=medium
+
+ * Initial debian packaging for layer23
+
+ -- Oliver Smith <osmith@sysmocom.de> Mon, 19 Mar 2024 11:38:22 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 00000000..f599e28b
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+10
diff --git a/debian/control b/debian/control
new file mode 100644
index 00000000..c90b6a7c
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,29 @@
+Source: osmocom-bb
+Section: net
+Priority: optional
+Maintainer: Osmocom team <openbsc@lists.osmocom.org>
+Build-Depends: debhelper (>= 10),
+ dh-autoreconf,
+ autotools-dev,
+ autoconf,
+ automake,
+ libtool,
+ pkg-config,
+ libosmocore-dev (>= 1.5.0),
+ libosmo-csn1-dev,
+ libosmo-gprs-gmm-dev,
+ libosmo-gprs-llc-dev,
+ libosmo-gprs-rlcmac-dev,
+ libosmo-gprs-sm-dev,
+ libosmo-gprs-sndcp-dev,
+ libosmogapk-dev
+Standards-Version: 4.1.4
+Vcs-Browser: https://gitea.osmocom.org/phone-side/osmocom-bb
+Vcs-Git: https://gerrit.osmocom.org/osmocom-bb
+Homepage: https://osmocom.org/projects/baseband/wiki
+
+Package: osmocom-bb-layer23
+Architecture: any
+Multi-Arch: foreign
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Description: MS-side implementation of L2 and L3 GSM protocols
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 00000000..c9ee2c19
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,23 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: osmocom-bb
+Source: https://gitea.osmocom.org/phone-side/osmocom-bb
+
+Files: *
+License: GPL-2.0+
+
+License: GPL-2.0+
+ This package 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, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
diff --git a/debian/osmocom-bb.install b/debian/osmocom-bb.install
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/debian/osmocom-bb.install
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 00000000..8dd0df8d
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+
+%:
+ dh $@ -D src/host/layer23 --with-autoreconf
+
+override_dh_auto_configure:
+ dh_auto_configure -D src/host/layer23 -- --with-gapk-io
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 00000000..89ae9db8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/doc/examples/mobile/default.cfg b/doc/examples/mobile/default.cfg
index f14e9009..676c76fc 100644
--- a/doc/examples/mobile/default.cfg
+++ b/doc/examples/mobile/default.cfg
@@ -14,6 +14,8 @@ no hide-default
ms 1
layer2-socket /tmp/osmocom_l2
sap-socket /tmp/osmocom_sap
+ mncc-socket /tmp/ms_mncc_1
+ mncc-handler internal
sim reader
network-selection-mode auto
imei 000000000000000 0
@@ -51,6 +53,12 @@ ms 1
full-speech-v1
full-speech-v2
half-speech-v1
+ no full-data-14400
+ full-data-9600
+ full-data-4800
+ half-data-4800
+ full-data-2400
+ half-data-2400
min-rxlev -106
dsc-max 90
no skip-max-per-band
@@ -59,6 +67,19 @@ ms 1
ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
no barred-access
rplmn 001 01
- audio
- io-handler none
+ tch-data
+ io-handler unix-sock
+ io-tch-format ti
+ unix-socket /tmp/ms_data_1
+ call-params type-rate 71
+ call-params ce transparent
+ call-params async
+ call-params async nr-stop-bits 1
+ call-params async nr-data-bits 8
+ call-params async parity none
+ tch-voice
+ io-handler l1phy
+! io-format ti
+ alsa-output-dev default
+ alsa-input-dev default
no shutdown
diff --git a/doc/examples/mobile/multi_ms.cfg b/doc/examples/mobile/multi_ms.cfg
index c72817fd..48cdfb13 100644
--- a/doc/examples/mobile/multi_ms.cfg
+++ b/doc/examples/mobile/multi_ms.cfg
@@ -14,6 +14,8 @@ no hide-default
ms one
layer2-socket /tmp/osmocom_l2.one
sap-socket /tmp/osmocom_sap.one
+ mncc-socket /tmp/ms_mncc_one
+ mncc-handler internal
sim reader
network-selection-mode auto
imei 000000000000000 0
@@ -59,13 +61,22 @@ ms one
ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
no barred-access
rplmn 001 01
- audio
- io-handler none
+ tch-data
+ io-handler unix-sock
+ io-tch-format ti
+ unix-socket /tmp/ms_data.one
+ tch-voice
+ io-handler l1phy
+! io-format ti
+ alsa-output-dev default
+ alsa-input-dev default
no shutdown
!
ms two
layer2-socket /tmp/osmocom_l2.two
sap-socket /tmp/osmocom_sap.two
+ mncc-socket /tmp/ms_mncc_two
+ mncc-handler internal
sim reader
network-selection-mode auto
imei 000000000000000 0
@@ -111,6 +122,13 @@ ms two
ki comp128 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
no barred-access
rplmn 001 01
- audio
- io-handler none
+ tch-data
+ io-handler unix-sock
+ io-tch-format ti
+ unix-socket /tmp/ms_data.two
+ tch-voice
+ io-handler l1phy
+! io-format ti
+ alsa-output-dev default
+ alsa-input-dev default
no shutdown
diff --git a/doc/examples/modem/modem.cfg b/doc/examples/modem/modem.cfg
new file mode 100644
index 00000000..af98082c
--- /dev/null
+++ b/doc/examples/modem/modem.cfg
@@ -0,0 +1,24 @@
+!
+! OsmocomBB example configuration for modem application
+!!
+!
+line vty
+ no login
+!
+ms 1
+ imei 000000000000000 0
+ imei-fixed
+ sim test
+ test-sim
+ imsi 001010000000000
+ ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ no barred-access
+ rplmn 001 01
+ apn internet
+ tun-device modem4
+! tun-netns netns_modem4
+ type-support v4
+ no shutdown
+ no shutdown
+cpu-sched
+ policy rr 1
diff --git a/doc/manuals/Makefile b/doc/manuals/Makefile
index 603f4ba6..53bb375b 100644
--- a/doc/manuals/Makefile
+++ b/doc/manuals/Makefile
@@ -5,4 +5,5 @@ ASCIIDOC = osmocombb-usermanual.adoc
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
+OSMO_REPOSITORY = osmocom-bb
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h
index cf41ac74..79feabab 100644
--- a/include/l1ctl_proto.h
+++ b/include/l1ctl_proto.h
@@ -15,58 +15,52 @@
* 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.
- *
*/
#ifndef __L1CTL_PROTO_H__
#define __L1CTL_PROTO_H__
enum {
- _L1CTL_NONE = 0,
- L1CTL_FBSB_REQ,
- L1CTL_FBSB_CONF,
- L1CTL_DATA_IND,
- L1CTL_RACH_REQ,
- L1CTL_DM_EST_REQ,
- L1CTL_DATA_REQ,
- L1CTL_RESET_IND,
- L1CTL_PM_REQ, /* power measurement */
- L1CTL_PM_CONF, /* power measurement */
- L1CTL_ECHO_REQ,
- L1CTL_ECHO_CONF,
- L1CTL_RACH_CONF,
- L1CTL_RESET_REQ,
- L1CTL_RESET_CONF,
- L1CTL_DATA_CONF,
- L1CTL_CCCH_MODE_REQ,
- L1CTL_CCCH_MODE_CONF,
- L1CTL_DM_REL_REQ,
- L1CTL_PARAM_REQ,
- L1CTL_DM_FREQ_REQ,
- L1CTL_CRYPTO_REQ,
- L1CTL_SIM_REQ,
- L1CTL_SIM_CONF,
- L1CTL_TCH_MODE_REQ,
- L1CTL_TCH_MODE_CONF,
- L1CTL_NEIGH_PM_REQ,
- L1CTL_NEIGH_PM_IND,
- L1CTL_TRAFFIC_REQ,
- L1CTL_TRAFFIC_CONF,
- L1CTL_TRAFFIC_IND,
- L1CTL_BURST_IND,
-
- /* configure TBF for uplink/downlink */
- L1CTL_TBF_CFG_REQ,
- L1CTL_TBF_CFG_CONF,
-
- L1CTL_DATA_TBF_REQ,
- L1CTL_DATA_TBF_CONF,
-
+ _L1CTL_NONE = 0x00,
+ L1CTL_FBSB_REQ = 0x01,
+ L1CTL_FBSB_CONF = 0x02,
+ L1CTL_DATA_IND = 0x03,
+ L1CTL_RACH_REQ = 0x04,
+ L1CTL_DM_EST_REQ = 0x05,
+ L1CTL_DATA_REQ = 0x06,
+ L1CTL_RESET_IND = 0x07,
+ L1CTL_PM_REQ = 0x08, /* power measurement */
+ L1CTL_PM_CONF = 0x09, /* power measurement */
+ L1CTL_ECHO_REQ = 0x0a,
+ L1CTL_ECHO_CONF = 0x0b,
+ L1CTL_RACH_CONF = 0x0c,
+ L1CTL_RESET_REQ = 0x0d,
+ L1CTL_RESET_CONF = 0x0e,
+ L1CTL_DATA_CONF = 0x0f,
+ L1CTL_CCCH_MODE_REQ = 0x10,
+ L1CTL_CCCH_MODE_CONF = 0x11,
+ L1CTL_DM_REL_REQ = 0x12,
+ L1CTL_PARAM_REQ = 0x13,
+ L1CTL_DM_FREQ_REQ = 0x14,
+ L1CTL_CRYPTO_REQ = 0x15,
+ L1CTL_SIM_REQ = 0x16,
+ L1CTL_SIM_CONF = 0x17,
+ L1CTL_TCH_MODE_REQ = 0x18,
+ L1CTL_TCH_MODE_CONF = 0x19,
+ L1CTL_NEIGH_PM_REQ = 0x1a,
+ L1CTL_NEIGH_PM_IND = 0x1b,
+ L1CTL_TRAFFIC_REQ = 0x1c,
+ L1CTL_TRAFFIC_CONF = 0x1d,
+ L1CTL_TRAFFIC_IND = 0x1e,
+ L1CTL_BURST_IND = 0x1f,
+ L1CTL_GPRS_UL_TBF_CFG_REQ = 0x20,
+ L1CTL_GPRS_DL_TBF_CFG_REQ = 0x21,
+ L1CTL_GPRS_UL_BLOCK_REQ = 0x22,
+ L1CTL_GPRS_DL_BLOCK_IND = 0x23,
/* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */
- L1CTL_EXT_RACH_REQ,
+ L1CTL_EXT_RACH_REQ = 0x24,
+ L1CTL_GPRS_RTS_IND = 0x25,
+ L1CTL_GPRS_UL_BLOCK_CNF = 0x26,
};
enum ccch_mode {
@@ -82,23 +76,6 @@ enum neigh_mode {
NEIGH_MODE_SB,
};
-enum l1ctl_coding_scheme {
- L1CTL_CS_NONE,
- L1CTL_CS1,
- L1CTL_CS2,
- L1CTL_CS3,
- L1CTL_CS4,
- L1CTL_MCS1,
- L1CTL_MCS2,
- L1CTL_MCS3,
- L1CTL_MCS4,
- L1CTL_MCS5,
- L1CTL_MCS6,
- L1CTL_MCS7,
- L1CTL_MCS8,
- L1CTL_MCS9,
-};
-
/*
* NOTE: struct size. We do add manual padding out of the believe
* that it will avoid some unaligned access.
@@ -166,7 +143,11 @@ struct l1ctl_tch_mode_conf {
uint8_t tch_mode; /* enum tch_mode */
uint8_t audio_mode;
uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */
- uint8_t padding[1];
+ struct { /* 3GPP TS 08.58 9.3.52, 3GPP TS 44.018 10.5.2.21aa */
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
+ uint8_t tch_flags;
} __attribute__((packed));
/* data on the CCCH was found. This is following the header */
@@ -192,15 +173,6 @@ struct l1ctl_info_ul {
uint8_t payload[0];
} __attribute__((packed));
-struct l1ctl_info_ul_tbf {
- /* references l1ctl_tbf_cfg_req.tbf_nr */
- uint8_t tbf_nr;
- uint8_t coding_scheme;
- uint8_t padding[2];
- /* RLC/MAC block, size determines CS */
- uint8_t payload[0];
-} __attribute__((packed));
-
/*
* msg for FBSB_REQ
* the l1_info_ul header is in front
@@ -245,14 +217,21 @@ struct l1ctl_tch_mode_req {
#define AUDIO_RX_TRAFFIC_IND (1<<3)
uint8_t audio_mode;
uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */
- uint8_t padding[1];
+ struct { /* 3GPP TS 08.58 9.3.52, 3GPP TS 44.018 10.5.2.21aa */
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
+ uint8_t tch_flags;
} __attribute__((packed));
+#define L1CTL_TCH_FLAG_RXONLY (1<<0) /* TX disabled */
+
/* the l1_info_ul header is in front */
struct l1ctl_rach_req {
uint8_t ra;
uint8_t combined;
uint16_t offset;
+ uint8_t uic;
} __attribute__((packed));
@@ -292,6 +271,7 @@ struct l1ctl_dm_est_req {
};
uint8_t tch_mode;
uint8_t audio_mode;
+ uint8_t tch_flags;
} __attribute__((packed));
struct l1ctl_dm_freq_req {
@@ -373,15 +353,60 @@ struct l1ctl_traffic_req {
uint8_t data[0];
} __attribute__((packed));
-struct l1ctl_tbf_cfg_req {
- /* future support for multiple concurrent TBFs. 0 for now */
- uint8_t tbf_nr;
- /* is this about an UL TBF (1) or DL (0) */
- uint8_t is_uplink;
+/* payload of L1CTL_GPRS_UL_TBF_CFG_REQ */
+struct l1ctl_gprs_ul_tbf_cfg_req {
+ uint8_t tbf_ref;
+ uint8_t slotmask;
uint8_t padding[2];
+ uint32_t start_fn; /* TBF Starting Time (absolute Fn) */
+} __attribute__((packed));
+
+/* payload of L1CTL_GPRS_DL_TBF_CFG_REQ */
+struct l1ctl_gprs_dl_tbf_cfg_req {
+ uint8_t tbf_ref;
+ uint8_t slotmask;
+ uint8_t dl_tfi;
+ uint8_t padding[1];
+ uint32_t start_fn; /* TBF Starting Time (absolute Fn) */
+} __attribute__((packed));
- /* one USF for each TN, or 255 for invalid/unused */
- uint8_t usf[8];
+/* part of L1CTL_GPRS_{UL,DL}_BLOCK_{REQ,IND} */
+struct l1ctl_gprs_block_hdr {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t padding[3];
+} __attribute__((packed));
+
+/* payload of L1CTL_GPRS_UL_BLOCK_REQ */
+struct l1ctl_gprs_ul_block_req {
+ struct l1ctl_gprs_block_hdr hdr;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* payload of L1CTL_GPRS_DL_BLOCK_IND */
+struct l1ctl_gprs_dl_block_ind {
+ struct l1ctl_gprs_block_hdr hdr;
+ struct __attribute__((packed)) {
+ uint16_t ber10k; /* Bit Error Rate */
+ int16_t ci_cb; /* C/I in centiBels */
+ uint8_t rx_lev; /* RxLev 0..63 */
+ } meas;
+ uint8_t usf;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* payload of L1CTL_GPRS_RTS_IND */
+struct l1ctl_gprs_rts_ind {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t usf;
+} __attribute__((packed));
+
+/* payload of L1CTL_GPRS_UL_BLOCK_CNF */
+struct l1ctl_gprs_ul_block_cnf {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t data[0];
} __attribute__((packed));
#endif /* __L1CTL_PROTO_H__ */
diff --git a/include/l1gprs.h b/include/l1gprs.h
new file mode 100644
index 00000000..6a18da1f
--- /dev/null
+++ b/include/l1gprs.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <osmocom/core/linuxlist.h>
+
+struct l1gprs_state;
+struct msgb;
+
+struct l1gprs_tbf_pending_req {
+ /*! Item in l1gprs_state->tbf_list_pending */
+ struct llist_head list;
+ /*! Uplink or Downlink */
+ bool uplink;
+ /*! TBF reference number (not index) */
+ uint8_t tbf_ref;
+ /*! PDCH timeslots used by this TBF */
+ uint8_t slotmask;
+ /*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */
+ uint8_t dl_tfi;
+ /*! TBF starting time (absolute TDMA Fn) */
+ uint32_t start_fn;
+};
+
+struct l1gprs_tbf {
+ /*! Item in l1gprs_state->tbf_list */
+ struct llist_head list;
+ /*! Uplink or Downlink */
+ bool uplink;
+ /*! TBF reference number (not index) */
+ uint8_t tbf_ref;
+ /*! PDCH timeslots used by this TBF */
+ uint8_t slotmask;
+ /*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */
+ uint8_t dl_tfi;
+};
+
+struct l1gprs_pdch {
+ /*! Timeslot number */
+ uint8_t tn;
+ /*! Backpointer to l1gprs_state we belong to */
+ struct l1gprs_state *gprs;
+ /*! UL TBF count */
+ uint8_t ul_tbf_count;
+ /*! DL TBF count */
+ uint8_t dl_tbf_count;
+ /*! DL TFI mask */
+ uint32_t dl_tfi_mask;
+ /*! Pending UL TBF count */
+ uint8_t pending_ul_tbf_count;
+ /*! Pending DL TBF count */
+ uint8_t pending_dl_tbf_count;
+};
+
+static inline size_t l1gprs_pdch_use_count(const struct l1gprs_pdch *pdch)
+{
+ return pdch->ul_tbf_count + pdch->dl_tbf_count +
+ pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count;
+}
+
+
+typedef void (*l1gprs_pdch_changed_t)(struct l1gprs_pdch *pdch, bool active);
+
+struct l1gprs_state {
+ /*! PDCH state for each timeslot */
+ struct l1gprs_pdch pdch[8];
+ /*! Uplink and Downlink TBFs (active), struct l1gprs_pending_tbf */
+ struct llist_head tbf_list;
+ /*! Uplink and Downlink TBFs (pending), struct l1gprs_tbf_pending_req */
+ struct llist_head tbf_list_pending;
+ /*! Logging context (used as prefix for messages) */
+ char *log_prefix;
+ /*! Some private data for API user */
+ void *priv;
+ /*! Callback triggered to signal lower layers when a PDCH TS has to be activated/deactivated */
+ l1gprs_pdch_changed_t pdch_changed_cb;
+};
+
+void l1gprs_logging_init(int logc);
+struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv);
+void l1gprs_state_free(struct l1gprs_state *gprs);
+void l1gprs_state_set_pdch_changed_cb(struct l1gprs_state *gprs, l1gprs_pdch_changed_t pdch_changed_cb);
+
+int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg);
+int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg);
+
+struct l1gprs_prim_block_hdr {
+ uint32_t fn;
+ uint8_t tn;
+};
+
+struct l1gprs_prim_ul_block_req {
+ struct l1gprs_prim_block_hdr hdr;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+struct l1gprs_prim_dl_block_ind {
+ struct l1gprs_prim_block_hdr hdr;
+ struct {
+ uint16_t ber10k;
+ int16_t ci_cb;
+ uint8_t rx_lev;
+ } meas;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs,
+ struct l1gprs_prim_ul_block_req *req,
+ const struct msgb *msg);
+struct msgb *l1gprs_handle_ul_block_cnf(struct l1gprs_state *gprs,
+ uint32_t fn, uint8_t tn,
+ const uint8_t *data,
+ size_t data_len);
+struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs,
+ const struct l1gprs_prim_dl_block_ind *ind, uint8_t *usf);
+struct msgb *l1gprs_handle_rts_ind(struct l1gprs_state *gprs, uint32_t fn, uint8_t tn, uint8_t usf);
diff --git a/src/Makefile b/src/Makefile
index d92acbc1..0835a855 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -13,7 +13,7 @@ TOPDIR=$(shell pwd)
all: libosmocore-target nofirmware firmware mtk-firmware
-nofirmware: layer23 osmocon trxcon gsmmap gprsdecode virtphy
+nofirmware: layer23 osmocon trxcon gprsdecode virtphy
libosmocore-target: shared/libosmocore/build-target/src/.libs/libosmocore.a
@@ -71,19 +71,6 @@ host/trxcon/trxcon: host/trxcon/Makefile
make -C host/trxcon
-.PHONY: gsmmap
-gsmmap: host/gsmmap/gsmmap
-
-host/gsmmap/configure: host/gsmmap/configure.ac
- cd host/gsmmap && autoreconf -i
-
-host/gsmmap/Makefile: host/gsmmap/configure
- cd host/gsmmap && ./configure $(HOST_CONFARGS)
-
-host/gsmmap/gsmmap: host/gsmmap/Makefile
- make -C host/gsmmap
-
-
.PHONY: layer23
layer23: host/layer23/layer23
@@ -121,7 +108,6 @@ clean:
make -C shared/libosmocore/build-target $@
make -C host/layer23 $@
make -C host/osmocon $@
- make -C host/gsmmap $@
make -C host/gprsdecode $@
make -C host/virt_phy $@
make -C host/trxcon $@
@@ -132,7 +118,6 @@ distclean:
rm -rf shared/libosmocore/build-target
make -C host/layer23 $@
make -C host/osmocon $@
- make -C host/gsmmap $@
make -C host/gprsdecode $@
make -C host/virt_phy $@
make -C host/trxcon $@
diff --git a/src/README.development b/src/README.development
index 64821438..e09793b4 100644
--- a/src/README.development
+++ b/src/README.development
@@ -33,7 +33,7 @@ handling, timers - as well as some more specifically GSM related things
like a TLV parser, a Comp128V1 implementation and utility functions for
RSL (TS 08.58) and CC/MM/RR (TS 04.08).
-libosmocore is maintained in git://git.osmocom.org/libosmocore.git, so
+libosmocore is maintained in https://gitea.osmocom.org/osmocom/libosmocore, so
DO NOT DIRECTLY COMMIT TO libosmocore IN THIS REPOSITORY!
diff --git a/src/host/fb_tools/bdf_to_c.py b/src/host/fb_tools/bdf_to_c.py
index ebeb7f9c..f808f508 100755
--- a/src/host/fb_tools/bdf_to_c.py
+++ b/src/host/fb_tools/bdf_to_c.py
@@ -19,10 +19,6 @@ selected glyphs in the format defined by the <fb/font.h> header.
# 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.
from optparse import OptionParser
import sys
diff --git a/src/host/gprsdecode/Makefile.am b/src/host/gprsdecode/Makefile.am
index 542d54b6..b5c8b12a 100644
--- a/src/host/gprsdecode/Makefile.am
+++ b/src/host/gprsdecode/Makefile.am
@@ -6,8 +6,7 @@ AM_CPPFLAGS = \
$(NULL)
AM_CFLAGS = \
- -Wall -O3 \
- -Wno-missing-braces \
+ -Wall \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCODING_CFLAGS) \
diff --git a/src/host/gprsdecode/configure.ac b/src/host/gprsdecode/configure.ac
index 8da68c80..734cbd30 100644
--- a/src/host/gprsdecode/configure.ac
+++ b/src/host/gprsdecode/configure.ac
@@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script
AC_INIT([gprsdecode], [0.0.0])
AM_INIT_AUTOMAKE
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
diff --git a/src/host/gprsdecode/gprs.c b/src/host/gprsdecode/gprs.c
index ae59cf90..4ba91ea0 100644
--- a/src/host/gprsdecode/gprs.c
+++ b/src/host/gprsdecode/gprs.c
@@ -17,10 +17,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>
diff --git a/src/host/gprsdecode/gsmtap.c b/src/host/gprsdecode/gsmtap.c
index 5c124b23..ec816b35 100644
--- a/src/host/gprsdecode/gsmtap.c
+++ b/src/host/gprsdecode/gsmtap.c
@@ -17,10 +17,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>
@@ -100,6 +96,6 @@ void gsmtap_send_llc(uint8_t *data, size_t len, bool ul)
memcpy(dst, data, len);
/* Finally, send to the sink */
- gsmtap_sendmsg(gti, msg);
+ gsmtap_sendmsg_free(gti, msg);
}
diff --git a/src/host/gprsdecode/main.c b/src/host/gprsdecode/main.c
index 7e094896..49484e29 100644
--- a/src/host/gprsdecode/main.c
+++ b/src/host/gprsdecode/main.c
@@ -17,10 +17,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>
diff --git a/src/host/gprsdecode/rlcmac.c b/src/host/gprsdecode/rlcmac.c
index d33cb1eb..dc11fd30 100644
--- a/src/host/gprsdecode/rlcmac.c
+++ b/src/host/gprsdecode/rlcmac.c
@@ -17,10 +17,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>
diff --git a/src/host/gprsdecode/rlcmac.h b/src/host/gprsdecode/rlcmac.h
index 2381d771..41c0decd 100644
--- a/src/host/gprsdecode/rlcmac.h
+++ b/src/host/gprsdecode/rlcmac.h
@@ -2,6 +2,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include <osmocom/core/endian.h>
#define OLD_TIME 2000
@@ -16,10 +17,16 @@ struct gprs_message {
};
struct gprs_lime {
+#if OSMO_IS_LITTLE_ENDIAN
uint8_t li:6,
m:1,
e:1;
uint8_t used;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t e:1, m:1, li:6;
+ uint8_t used;
+#endif
} __attribute__ ((packed));
struct gprs_frag {
diff --git a/src/host/gsmmap/.gitignore b/src/host/gsmmap/.gitignore
deleted file mode 100644
index 661fd133..00000000
--- a/src/host/gsmmap/.gitignore
+++ /dev/null
@@ -1,35 +0,0 @@
-# autoreconf by-products
-*.in
-
-aclocal.m4
-autom4te.cache/
-configure
-depcomp
-install-sh
-missing
-
-# configure by-products
-.deps/
-Makefile
-
-config.status
-version.h
-
-# build by-products
-*.o
-
-gsmmap
-
-# various
-.version
-.tarball-version
-
-# IDA file
-*.id*
-*.nam
-*.til
-
-# Other test files
-*.dump
-*.bin
-*.log
diff --git a/src/host/gsmmap/Makefile.am b/src/host/gsmmap/Makefile.am
deleted file mode 100644
index 29be15c5..00000000
--- a/src/host/gsmmap/Makefile.am
+++ /dev/null
@@ -1,17 +0,0 @@
-AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
-
-# versioning magic
-BUILT_SOURCES = $(top_srcdir)/.version
-$(top_srcdir)/.version:
- echo $(VERSION) > $@-t && mv $@-t $@
-dist-hook:
- echo $(VERSION) > $(distdir)/.tarball-version
-
-INCLUDES = $(all_includes) -I../layer23/include -DHOST_BUILD
-AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS)
-
-sbin_PROGRAMS = gsmmap
-
-gsmmap_SOURCES = gsmmap.c geo.c locate.c log.c ../layer23/src/common/sysinfo.c ../layer23/src/common/networks.c ../layer23/src/common/logging.c
-gsmmap_LDADD = $(LIBOSMOGSM_LIBS) $(LIBOSMOCORE_LIBS) -lm
-
diff --git a/src/host/gsmmap/configure.ac b/src/host/gsmmap/configure.ac
deleted file mode 100644
index 3a42d4c3..00000000
--- a/src/host/gsmmap/configure.ac
+++ /dev/null
@@ -1,26 +0,0 @@
-dnl Process this file with autoconf to produce a configure script
-AC_INIT([gsmmap],
- m4_esyscmd([./git-version-gen .tarball-version]),
- [baseband-devel@lists.osmocom.org])
-
-AM_INIT_AUTOMAKE([dist-bzip2])
-
-dnl kernel style compile messages
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-
-dnl checks for programs
-AC_PROG_MAKE_SET
-AC_PROG_CC
-AC_PROG_INSTALL
-
-dnl checks for libraries
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
-PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm)
-
-dnl checks for header files
-AC_HEADER_STDC
-
-dnl Checks for typedefs, structures and compiler characteristics
-
-AC_OUTPUT(
- Makefile)
diff --git a/src/host/gsmmap/geo.c b/src/host/gsmmap/geo.c
deleted file mode 100644
index 65633d2c..00000000
--- a/src/host/gsmmap/geo.c
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <math.h>
-#include "geo.h"
-
-void geo2space(double *x, double *y, double *z, double lon, double lat)
-{
- *z = sin(lat / 180.0 * PI) * POLE_RADIUS;
- *x = sin(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS;
- *y = -cos(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS;
-}
-
-void space2geo(double *lon, double *lat, double x, double y, double z)
-{
- double r;
-
- /* bring geoid to 1m radius */
- z = z / POLE_RADIUS;
- x = x / EQUATOR_RADIUS;
- y = y / EQUATOR_RADIUS;
-
- /* normalize */
- r = sqrt(x * x + y * y + z * z);
- z = z / r;
- x = x / r;
- y = y / r;
-
- *lat = asin(z) / PI * 180;
- *lon = atan2(x, -y) / PI * 180;
-}
-
-double distinspace(double x1, double y1, double z1, double x2, double y2,
- double z2)
-{
- double x = x1 - x2;
- double y = y1 - y2;
- double z = z1 - z2;
-
- return sqrt(x * x + y * y + z * z);
-}
-
-double distonplane(double x1, double y1, double x2, double y2)
-{
- double x = x1 - x2;
- double y = y1 - y2;
-
- return sqrt(x * x + y * y);
-}
-
diff --git a/src/host/gsmmap/git-version-gen b/src/host/gsmmap/git-version-gen
deleted file mode 100755
index 652fac68..00000000
--- a/src/host/gsmmap/git-version-gen
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/bin/sh
-# Print a version string.
-scriptversion=2010-01-28.01
-
-# Copyright (C) 2007-2010 Free Software Foundation, Inc.
-#
-# 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/>.
-
-# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
-# It may be run two ways:
-# - from a git repository in which the "git describe" command below
-# produces useful output (thus requiring at least one signed tag)
-# - from a non-git-repo directory containing a .tarball-version file, which
-# presumes this script is invoked like "./git-version-gen .tarball-version".
-
-# In order to use intra-version strings in your project, you will need two
-# separate generated version string files:
-#
-# .tarball-version - present only in a distribution tarball, and not in
-# a checked-out repository. Created with contents that were learned at
-# the last time autoconf was run, and used by git-version-gen. Must not
-# be present in either $(srcdir) or $(builddir) for git-version-gen to
-# give accurate answers during normal development with a checked out tree,
-# but must be present in a tarball when there is no version control system.
-# Therefore, it cannot be used in any dependencies. GNUmakefile has
-# hooks to force a reconfigure at distribution time to get the value
-# correct, without penalizing normal development with extra reconfigures.
-#
-# .version - present in a checked-out repository and in a distribution
-# tarball. Usable in dependencies, particularly for files that don't
-# want to depend on config.h but do want to track version changes.
-# Delete this file prior to any autoconf run where you want to rebuild
-# files to pick up a version string change; and leave it stale to
-# minimize rebuild time after unrelated changes to configure sources.
-#
-# It is probably wise to add these two files to .gitignore, so that you
-# don't accidentally commit either generated file.
-#
-# Use the following line in your configure.ac, so that $(VERSION) will
-# automatically be up-to-date each time configure is run (and note that
-# since configure.ac no longer includes a version string, Makefile rules
-# should not depend on configure.ac for version updates).
-#
-# AC_INIT([GNU project],
-# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
-# [bug-project@example])
-#
-# Then use the following lines in your Makefile.am, so that .version
-# will be present for dependencies, and so that .tarball-version will
-# exist in distribution tarballs.
-#
-# BUILT_SOURCES = $(top_srcdir)/.version
-# $(top_srcdir)/.version:
-# echo $(VERSION) > $@-t && mv $@-t $@
-# dist-hook:
-# echo $(VERSION) > $(distdir)/.tarball-version
-
-case $# in
- 1) ;;
- *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
-esac
-
-tarball_version_file=$1
-nl='
-'
-
-# First see if there is a tarball-only version file.
-# then try "git describe", then default.
-if test -f $tarball_version_file
-then
- v=`cat $tarball_version_file` || exit 1
- case $v in
- *$nl*) v= ;; # reject multi-line output
- [0-9]*) ;;
- *) v= ;;
- esac
- test -z "$v" \
- && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
-fi
-
-if test -n "$v"
-then
- : # use $v
-elif
- v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \
- || git describe --abbrev=4 HEAD 2>/dev/null` \
- && case $v in
- osmocon_[0-9]*) ;;
- osmocon_v[0-9]*) ;;
- *) (exit 1) ;;
- esac
-then
- # Is this a new git that lists number of commits since the last
- # tag or the previous older version that did not?
- # Newer: v6.10-77-g0f8faeb
- # Older: v6.10-g0f8faeb
- case $v in
- *-*-*) : git describe is okay three part flavor ;;
- *-*)
- : git describe is older two part flavor
- # Recreate the number of commits and rewrite such that the
- # result is the same as if we were using the newer version
- # of git describe.
- vtag=`echo "$v" | sed 's/-.*//'`
- numcommits=`git rev-list "$vtag"..HEAD | wc -l`
- v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
- ;;
- esac
-
- # Change the first '-' to a '.', so version-comparing tools work properly.
- # Remove the "g" in git describe's output string, to save a byte.
- v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`;
-else
- v="UNKNOWN"
-fi
-
-v=`echo "$v" |sed 's/^v//'`
-
-# Don't declare a version "dirty" merely because a time stamp has changed.
-git status > /dev/null 2>&1
-
-dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
-case "$dirty" in
- '') ;;
- *) # Append the suffix only if there isn't one already.
- case $v in
- *-dirty) ;;
- *) v="$v-dirty" ;;
- esac ;;
-esac
-
-# Omit the trailing newline, so that m4_esyscmd can use the result directly.
-echo "$v" | tr -d '\012'
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore
index 8fb93f73..333c28c5 100644
--- a/src/host/layer23/.gitignore
+++ b/src/host/layer23/.gitignore
@@ -33,4 +33,6 @@ src/misc/echo_test
src/misc/cbch_sniff
src/misc/ccch_scan
src/misc/layer23
+src/misc/gsmmap
src/mobile/mobile
+src/modem/modem
diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac
index 3e696103..7fb8bf16 100644
--- a/src/host/layer23/configure.ac
+++ b/src/host/layer23/configure.ac
@@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script
AC_INIT([layer23], [0.0.0])
AM_INIT_AUTOMAKE
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -32,6 +34,7 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
@@ -39,36 +42,72 @@ then
fi
dnl checks for libraries
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm)
+PKG_CHECK_MODULES(LIBOSMOISDN, libosmoisdn)
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec)
+PKG_CHECK_MODULES(LIBOSMOGPRSRLCMAC, libosmo-gprs-rlcmac)
+PKG_CHECK_MODULES(LIBOSMOGPRSLLC, libosmo-gprs-llc)
+PKG_CHECK_MODULES(LIBOSMOGPRSSNDCP, libosmo-gprs-sndcp)
+PKG_CHECK_MODULES(LIBOSMOGPRSGMM, libosmo-gprs-gmm)
+PKG_CHECK_MODULES(LIBOSMOGPRSSM, libosmo-gprs-sm)
AC_CHECK_LIB(gps, gps_waiting, LIBGPS_CFLAGS=" -D_HAVE_GPSD" LIBGPS_LIBS=" -lgps ",,)
AC_SUBST([LIBGPS_CFLAGS])
AC_SUBST([LIBGPS_LIBS])
dnl optional dependencies
-PKG_CHECK_MODULES(LIBLUA, lua53, [
- WITH_LUA=1], [
- WITH_LUA=0])
-AC_SUBST([WITH_LUA])
-AM_CONDITIONAL([BUILD_LUA], test "x$WITH_LUA" = "x1")
+AC_ARG_WITH([lua53], [
+ AS_HELP_STRING([--with-lua53],
+ [Enable LUA scripting support @<:@default=check@:>@])
+])
+
+AC_ARG_WITH([gapk_io], [
+ AS_HELP_STRING([--with-gapk-io],
+ [Enable GAPK I/O support @<:@default=check@:>@])
+])
+
+found_lua53=no
+AS_IF([test "x$with_lua53" != "xno"], [
+ PKG_CHECK_MODULES(LIBLUA, lua53, [found_lua53=yes], [found_lua53=no])
+ AS_IF([test "x$with_lua53" = "xyes" -a "x$found_lua53" = "xno"], [
+ AC_MSG_ERROR([lua53 support requested but pkg-config is unable to find it])
+ ])
+])
+AM_CONDITIONAL([BUILD_LUA], test "x$found_lua53" = "xyes")
+
+found_gapk=no
+AS_IF([test "x$with_gapk_io" != "xno"], [
+ PKG_CHECK_MODULES(LIBOSMOGAPK, libosmogapk, [found_gapk=yes], [found_gapk=no])
+ AS_IF([test "x$with_gapk_io" = "xyes" -a "x$found_gapk" = "xno"], [
+ AC_MSG_ERROR([GAPK I/O support requested but pkg-config is unable to find it])
+ ])
+])
+AM_CONDITIONAL([BUILD_GAPK], test "x$found_gapk" = "xyes")
dnl checks for header files
AC_HEADER_STDC
dnl Checks for typedefs, structures and compiler characteristics
+AC_MSG_RESULT([CFLAGS="$CFLAGS"])
+AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
+
+dnl Generate the output
+AM_CONFIG_HEADER(config.h)
+
AC_OUTPUT(
src/Makefile
src/common/Makefile
src/misc/Makefile
src/mobile/Makefile
+ src/modem/Makefile
include/Makefile
include/osmocom/Makefile
include/osmocom/bb/Makefile
include/osmocom/bb/common/Makefile
include/osmocom/bb/misc/Makefile
include/osmocom/bb/mobile/Makefile
+ include/osmocom/bb/modem/Makefile
Makefile)
diff --git a/src/host/layer23/include/osmocom/bb/Makefile.am b/src/host/layer23/include/osmocom/bb/Makefile.am
index 58a5f7fb..3b6a4d8b 100644
--- a/src/host/layer23/include/osmocom/bb/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/Makefile.am
@@ -1 +1 @@
-SUBDIRS = common misc mobile
+SUBDIRS = common misc mobile modem
diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am
index f9364cd9..7c0fa972 100644
--- a/src/host/layer23/include/osmocom/bb/common/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am
@@ -1,3 +1,22 @@
-noinst_HEADERS = l1ctl.h l1l2_interface.h l23_app.h logging.h \
- networks.h gps.h sysinfo.h osmocom_data.h utils.h \
- sap_proto.h sap_fsm.h sap_interface.h sim.h
+noinst_HEADERS = \
+ apn.h \
+ apn_fsm.h \
+ l1ctl.h \
+ l1l2_interface.h \
+ l23_app.h \
+ logging.h \
+ ms.h \
+ networks.h \
+ gps.h \
+ sysinfo.h \
+ osmocom_data.h \
+ utils.h \
+ sap_proto.h \
+ sap_fsm.h \
+ sap_interface.h \
+ settings.h \
+ sim.h \
+ subscriber.h \
+ support.h \
+ vty.h \
+ $(NULL)
diff --git a/src/host/layer23/include/osmocom/bb/common/apn.h b/src/host/layer23/include/osmocom/bb/common/apn.h
new file mode 100644
index 00000000..0adb8de7
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/apn.h
@@ -0,0 +1,82 @@
+/* APN Context
+ * (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
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/tun.h>
+
+#include <osmocom/gprs/sm/sm.h>
+
+#include <osmocom/bb/common/apn_fsm.h>
+
+struct osmocom_ms;
+
+#define APN_TYPE_IPv4 0x01 /* v4-only */
+#define APN_TYPE_IPv6 0x02 /* v6-only */
+#define APN_TYPE_IPv4v6 0x04 /* v4v6 dual-stack */
+
+struct osmobb_pdp_ctx {
+ uint8_t nsapi;
+ uint8_t llc_sapi;
+ uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN];
+ uint8_t qos_len;
+ uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN];
+ uint8_t pco_len;
+ enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type;
+ struct osmo_sockaddr pdp_addr_v4;
+ struct osmo_sockaddr pdp_addr_v6;
+};
+
+struct osmobb_apn {
+ /* list of APNs inside MS */
+ struct llist_head list;
+ /* back-pointer to MS */
+ struct osmocom_ms *ms;
+
+ bool started;
+
+ struct {
+ /* Primary name */
+ char *name;
+ /* name of the network device */
+ char *dev_name;
+ /* netns name of the network device, NULL = default netns */
+ char *dev_netns_name;
+ /* types supported address types on this APN */
+ uint32_t apn_type_mask;
+ /* administratively shutdown (true) or not (false) */
+ bool shutdown;
+ /* transmit G-PDU sequence numbers (true) or not (false) */
+ bool tx_gpdu_seq;
+ } cfg;
+ struct osmo_tundev *tun;
+ struct apn_fsm_ctx fsm;
+ struct osmobb_pdp_ctx pdp;
+};
+
+struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name);
+void apn_free(struct osmobb_apn *apn);
+int apn_start(struct osmobb_apn *apn);
+int apn_stop(struct osmobb_apn *apn);
+
+#define LOGPAPN(level, apn, fmt, args...) \
+ LOGP(DTUN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
+
+#define LOGTUN(level, tun, fmt, args...) \
+ LOGP(DTUN, level, "TUN(%s): " fmt, osmo_tundev_get_name(tun), ## args)
diff --git a/src/host/layer23/include/osmocom/bb/common/apn_fsm.h b/src/host/layer23/include/osmocom/bb/common/apn_fsm.h
new file mode 100644
index 00000000..890267c9
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/apn_fsm.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <osmocom/core/fsm.h>
+
+struct osmobb_apn;
+
+enum apn_fsm_states {
+ APN_ST_DISABLED,
+ APN_ST_INACTIVE,
+ APN_ST_ACTIVATING,
+ APN_ST_ACTIVE,
+};
+
+enum apn_fsm_events {
+ APN_EV_GPRS_ALLOWED, /* data: bool *allowed */
+ APN_EV_GMM_ATTACHED,
+ APN_EV_GMM_DETACHED,
+ APN_EV_RX_SM_ACT_PDP_CTX_REJ, /* data: enum gsm48_gsm_cause *cause */
+ APN_EV_RX_SM_ACT_PDP_CTX_ACC,
+ APN_EV_RX_SM_DEACT_PDP_CTX_ACC,
+};
+
+struct apn_fsm_ctx {
+ struct osmo_fsm_inst *fi;
+ struct osmobb_apn *apn;
+};
+
+int apn_fsm_ctx_init(struct apn_fsm_ctx *ctx, struct osmobb_apn *apn);
+void apn_fsm_ctx_release(struct apn_fsm_ctx *ctx);
diff --git a/src/host/layer23/include/osmocom/bb/common/gps.h b/src/host/layer23/include/osmocom/bb/common/gps.h
index 58c0c536..e7ce915c 100644
--- a/src/host/layer23/include/osmocom/bb/common/gps.h
+++ b/src/host/layer23/include/osmocom/bb/common/gps.h
@@ -13,10 +13,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.
- *
*/
enum {
diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h
index 02ffa343..a74c9c37 100644
--- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h
+++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h
@@ -2,6 +2,7 @@
#define osmocom_l1ctl_h
#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
#include <osmocom/bb/common/osmocom_data.h>
struct osmocom_ms;
@@ -20,15 +21,15 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr,
uint8_t algo, uint8_t *key, uint8_t len);
/* Transmit L1CTL_RACH_REQ */
-int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset,
- uint8_t combined);
+int l1ctl_tx_rach_req(struct osmocom_ms *ms,
+ uint8_t chan_nr, uint8_t link_id,
+ uint8_t ra, uint16_t offset, uint8_t combined, uint8_t uic);
/* Transmit L1CTL_DM_EST_REQ */
-int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn,
- uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode);
-int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn,
- uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc,
- uint8_t tch_mode, uint8_t audio_mode);
+int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, uint8_t chan_nr, uint8_t tsc,
+ uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags);
+int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, uint16_t *ma, uint8_t ma_len,
+ uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags);
/* Transmit L1CTL_DM_FREQ_REQ */
int l1ctl_tx_dm_freq_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn,
@@ -48,8 +49,8 @@ int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn,
int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode);
/* Transmit TCH_MODE_REQ */
-int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode,
- uint8_t audio_mode, uint8_t tch_loop_mode);
+int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags,
+ uint8_t tch_loop_mode);
/* Transmit ECHO_REQ */
int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len);
@@ -73,4 +74,17 @@ int l1ctl_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
/* Transmit L1CTL_NEIGH_PM_REQ */
int l1ctl_tx_neigh_pm_req(struct osmocom_ms *ms, int num, uint16_t *arfcn);
+/* Transmit L1CTL_GPRS_UL_BLOCK_REQ */
+int l1ctl_tx_gprs_ul_block_req(struct osmocom_ms *ms, uint32_t fn, uint8_t tn,
+ const uint8_t *data, size_t data_len);
+
+/* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */
+int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref,
+ uint8_t slotmask, uint32_t start_fn);
+
+/* Transmit L1CTL_GPRS_DL_TBF_CFG_REQ */
+int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref,
+ uint8_t slotmask, uint32_t start_fn,
+ uint8_t dl_tfi);
+
#endif
diff --git a/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h b/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h
index 41403d87..9cb993c8 100644
--- a/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h
+++ b/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h
@@ -1,6 +1,10 @@
#ifndef _L1L2_INTERFACE_H
#define _L1L2_INTERFACE_H
+#include <osmocom/core/msgb.h>
+
+#define L2_DEFAULT_SOCKET_PATH "/tmp/osmocom_l2"
+
int layer2_open(struct osmocom_ms *ms, const char *socket_path);
int layer2_close(struct osmocom_ms *ms);
int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg);
diff --git a/src/host/layer23/include/osmocom/bb/common/l23_app.h b/src/host/layer23/include/osmocom/bb/common/l23_app.h
index 0b9994c3..6cf0b7e9 100644
--- a/src/host/layer23/include/osmocom/bb/common/l23_app.h
+++ b/src/host/layer23/include/osmocom/bb/common/l23_app.h
@@ -1,36 +1,77 @@
#ifndef _L23_APP_H
#define _L23_APP_H
+#include <osmocom/core/tun.h>
+#include <osmocom/core/gsmtap.h>
+
struct option;
+struct vty_app_info;
/* Options supported by the l23 app */
enum {
- L23_OPT_SAP = 1,
- L23_OPT_ARFCN = 2,
- L23_OPT_TAP = 4,
- L23_OPT_VTY = 8,
- L23_OPT_DBG = 16,
- L23_OPT_VTYIP = 32,
+ L23_OPT_SAP = 1 << 0,
+ L23_OPT_ARFCN = 1 << 1,
+ L23_OPT_TAP = 1 << 2,
+ L23_OPT_VTY = 1 << 3,
+ L23_OPT_DBG = 1 << 4,
};
-/* initialization, called once when starting the app, before entering
- * select loop */
-extern int l23_app_init(struct osmocom_ms *ms);
-extern int (*l23_app_work) (struct osmocom_ms *ms);
-extern int (*l23_app_exit) (struct osmocom_ms *ms);
+/* see (struct l23_global_config)->gsmtap.categ_gprs_mask */
+enum l23_gsmtap_gprs_category {
+ L23_GSMTAP_GPRS_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */
+ L23_GSMTAP_GPRS_C_DL_DUMMY = 1, /* downlink dummy blocks */
+ L23_GSMTAP_GPRS_C_DL_CTRL = 2, /* downlink control blocks */
+ L23_GSMTAP_GPRS_C_DL_DATA_GPRS = 3, /* downlink GPRS data blocks */
+ L23_GSMTAP_GPRS_C_DL_DATA_EGPRS = 4, /* downlink EGPRS data blocks */
+
+ L23_GSMTAP_GPRS_C_UL_UNKNOWN = 5, /* unknown or undecodable uplink blocks */
+ L23_GSMTAP_GPRS_C_UL_DUMMY = 6, /* uplink dummy blocks */
+ L23_GSMTAP_GPRS_C_UL_CTRL = 7, /* uplink control blocks */
+ L23_GSMTAP_GPRS_C_UL_DATA_GPRS = 8, /* uplink GPRS data blocks */
+ L23_GSMTAP_GPRS_C_UL_DATA_EGPRS = 9, /* uplink EGPRS data blocks */
+};
+
+struct l23_global_config {
+ struct {
+ char *remote_host;
+ char *local_host;
+ uint32_t lchan_mask; /* see l23_gsmtap_gprs_category */
+ uint32_t lchan_acch_mask; /* see l23_gsmtap_gprs_category */
+ bool lchan_acch;
+ uint32_t categ_gprs_mask;
+ struct gsmtap_inst *inst;
+ } gsmtap;
+};
+extern struct l23_global_config l23_cfg;
+
+extern void *l23_ctx;
+
+/* initialization, called once when starting the app, before reading VTY config */
+extern int l23_app_init(void);
+
+/* Start work after reading VTY config and starting layer23 components,
+ * immediately before entering main select loop */
+extern int (*l23_app_start)(void);
+
+extern int (*l23_app_work)(void);
+extern int (*l23_app_exit)(void);
/* configuration options */
struct l23_app_info {
const char *copyright;
const char *contribution;
+ struct vty_app_info *vty_info; /* L23_OPT_VTY */
char *getopt_string;
- int (*cfg_supported)();
+ uint32_t opt_supported; /* mask of L23_OPT_* */
int (*cfg_print_help)();
int (*cfg_getopt_opt)(struct option **options);
int (*cfg_handle_opt)(int c,const char *optarg);
+ int (*vty_init)(void);
+ osmo_tundev_data_ind_cb_t tun_data_ind_cb;
};
-extern struct l23_app_info *l23_app_info();
+/* all l23 apps must define this structure */
+extern const struct l23_app_info l23_app_info;
#endif /* _L23_APP_H */
diff --git a/src/host/layer23/include/osmocom/bb/common/logging.h b/src/host/layer23/include/osmocom/bb/common/logging.h
index bf6e6aa0..3920d2e0 100644
--- a/src/host/layer23/include/osmocom/bb/common/logging.h
+++ b/src/host/layer23/include/osmocom/bb/common/logging.h
@@ -12,6 +12,8 @@ enum {
DNB,
DMM,
DCC,
+ DGCC,
+ DBCC,
DSS,
DSMS,
DMNCC,
@@ -25,6 +27,14 @@ enum {
DMOB,
DPRIM,
DLUA,
+ DGAPK,
+ DCSD,
+ DTUN,
+ DRLCMAC,
+ DLLC,
+ DSNDCP,
+ DGMM,
+ DSM
};
extern const struct log_info log_info;
diff --git a/src/host/layer23/include/osmocom/bb/common/ms.h b/src/host/layer23/include/osmocom/bb/common/ms.h
new file mode 100644
index 00000000..36d3e3ba
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/ms.h
@@ -0,0 +1,116 @@
+/* Mobile Station */
+#pragma once
+
+#include <osmocom/core/select.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/write_queue.h>
+
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/bb/common/settings.h>
+#include <osmocom/bb/common/subscriber.h>
+#include <osmocom/bb/common/support.h>
+#include <osmocom/bb/common/sap_interface.h>
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
+#include <osmocom/bb/common/sysinfo.h>
+#include <osmocom/bb/mobile/gsm322.h>
+#include <osmocom/bb/mobile/gsm48_mm.h>
+#include <osmocom/bb/mobile/gsm48_cc.h>
+#include <osmocom/bb/mobile/mncc_sock.h>
+#include <osmocom/bb/common/sim.h>
+#include <osmocom/bb/common/l1ctl.h>
+
+struct osmobb_ms_gmm_layer {
+ uint8_t ac_ref_nr;
+ uint8_t key_seq;
+ uint8_t rand[16];
+ uint32_t tlli;
+};
+
+struct osmosap_entity {
+ struct osmo_fsm_inst *fi;
+ uint16_t max_msg_size;
+
+ /* Current state of remote SIM card */
+ enum sap_card_status_type card_status;
+
+ /* Optional SAP message call-back */
+ sap_msg_cb_t sap_msg_cb;
+ /* Optional response call-back */
+ sap_rsp_cb_t sap_rsp_cb;
+};
+
+struct osmol1_entity {
+ int (*l1_traffic_ind)(struct osmocom_ms *ms, struct msgb *msg);
+ int (*l1_gprs_ul_block_cnf)(struct osmocom_ms *ms, struct msgb *msg);
+ int (*l1_gprs_dl_block_ind)(struct osmocom_ms *ms, struct msgb *msg);
+ int (*l1_gprs_rts_ind)(struct osmocom_ms *ms, struct msgb *msg);
+};
+
+struct osmomncc_entity {
+ int (*mncc_recv)(struct osmocom_ms *ms, int msg_type, void *arg);
+ struct mncc_sock_state *sock_state;
+ uint32_t ref;
+};
+
+/* RX measurement statistics */
+struct rx_meas_stat {
+ uint32_t last_fn;
+
+ /* cumulated values of current cell from SACCH dl */
+ uint32_t frames;
+ uint32_t snr;
+ uint32_t berr;
+ uint32_t rxlev;
+
+ /* counters loss criterion */
+ int16_t dsc, ds_fail;
+ int16_t s, rl_fail;
+};
+
+enum osmobb_ms_shutdown_st {
+ MS_SHUTDOWN_NONE = 0,
+ MS_SHUTDOWN_IMSI_DETACH = 1,
+ MS_SHUTDOWN_WAIT_RESET = 2,
+ MS_SHUTDOWN_COMPL = 3,
+};
+
+struct osmocom_ms {
+ struct llist_head entity;
+ char *name;
+ struct osmo_wqueue l2_wq, sap_wq;
+ uint16_t test_arfcn;
+ struct osmol1_entity l1_entity;
+
+ bool started, deleting;
+ enum osmobb_ms_shutdown_st shutdown;
+ struct gsm_support support;
+ struct gsm_settings settings;
+ struct gsm_subscriber subscr;
+ struct gsm_sim sim;
+ struct lapdm_channel lapdm_channel;
+ struct osmosap_entity sap_entity;
+ struct rx_meas_stat meas;
+ struct gsm48_rrlayer rrlayer;
+ struct gsm322_plmn plmn;
+ struct gsm322_cellsel cellsel;
+ struct gsm48_mmlayer mmlayer;
+ struct gsm48_cclayer cclayer;
+ struct osmomncc_entity mncc_entity;
+ struct llist_head trans_list;
+
+ /* GPRS */
+ struct gprs_settings gprs;
+ struct osmobb_ms_gmm_layer gmmlayer;
+ struct osmo_fsm_inst *grr_fi;
+
+ struct tch_state *tch_state;
+
+ void *lua_state;
+ int lua_cb_ref;
+ char *lua_script;
+};
+
+struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name);
+
+extern uint16_t cfg_test_arfcn;
diff --git a/src/host/layer23/include/osmocom/bb/common/networks.h b/src/host/layer23/include/osmocom/bb/common/networks.h
index d34f3162..408e17d0 100644
--- a/src/host/layer23/include/osmocom/bb/common/networks.h
+++ b/src/host/layer23/include/osmocom/bb/common/networks.h
@@ -1,24 +1,27 @@
#ifndef _NETWORKS_H
#define _NETWORKS_H
+#include <osmocom/gsm/gsm23003.h>
-#define GSM_INPUT_INVALID 0xffff
-
+/* Instead of handling numerical MCC and MNC, they stored and handled
+ * hexadecimal. This makes it possible
+ * to correctly handle 2 and 3 digits MNC. Internally 2 digit MNCs are stored
+ * as 0xXXf, and 3 digits MNC are stored as 0xXXX, where X is the digit 0..9.
+*/
struct gsm_networks {
- uint16_t mcc;
- int16_t mnc;
+ uint16_t mcc_hex;
+ int16_t mnc_hex;
const char *name;
};
int gsm_match_mcc(uint16_t mcc, char *imsi);
-int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi);
-const char *gsm_print_mcc(uint16_t mcc);
-const char *gsm_print_mnc(uint16_t mcc);
+int gsm_match_mnc(uint16_t mcc, uint16_t mnc, bool mnc_3_digits, char *imsi);
const char *gsm_get_mcc(uint16_t mcc);
-const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc);
+const char *gsm_get_mnc(const struct osmo_plmn_id *plmn);
const char *gsm_imsi_mcc(char *imsi);
const char *gsm_imsi_mnc(char *imsi);
-const uint16_t gsm_input_mcc(char *string);
-const uint16_t gsm_input_mnc(char *string);
+
+uint16_t gsm_mcc_to_hex(uint16_t mcc);
+uint16_t gsm_mnc_to_hex(uint16_t mnc, bool mnc_3_digits);
#endif /* _NETWORKS_H */
diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
index 14e594cb..935c02b2 100644
--- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
+++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
@@ -1,107 +1,17 @@
-#ifndef osmocom_data_h
-#define osmocom_data_h
+#pragma once
-#include <osmocom/core/select.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/write_queue.h>
+#include <stdint.h>
+#include <stdbool.h>
struct osmocom_ms;
-
- /* FIXME no 'mobile' specific stuff should be here */
-#include <osmocom/bb/mobile/support.h>
-#include <osmocom/bb/mobile/settings.h>
-#include <osmocom/bb/mobile/subscriber.h>
-#include <osmocom/gsm/lapdm.h>
-#include <osmocom/bb/common/sap_interface.h>
-#include <osmocom/bb/common/sap_proto.h>
-#include <osmocom/bb/mobile/gsm48_rr.h>
-#include <osmocom/bb/common/sysinfo.h>
-#include <osmocom/bb/mobile/gsm322.h>
-#include <osmocom/bb/mobile/gsm48_mm.h>
-#include <osmocom/bb/mobile/gsm48_cc.h>
-#include <osmocom/bb/mobile/mncc_sock.h>
-#include <osmocom/bb/common/sim.h>
-#include <osmocom/bb/common/l1ctl.h>
-
-struct osmosap_entity {
- struct osmo_fsm_inst *fi;
- uint16_t max_msg_size;
-
- /* Current state of remote SIM card */
- enum sap_card_status_type card_status;
-
- /* Optional SAP message call-back */
- sap_msg_cb_t sap_msg_cb;
- /* Optional response call-back */
- sap_rsp_cb_t sap_rsp_cb;
-};
-
-struct osmol1_entity {
- int (*l1_traffic_ind)(struct osmocom_ms *ms, struct msgb *msg);
-};
-
-struct osmomncc_entity {
- int (*mncc_recv)(struct osmocom_ms *ms, int msg_type, void *arg);
- struct mncc_sock_state *sock_state;
- uint32_t ref;
-};
-
-
-/* RX measurement statistics */
-struct rx_meas_stat {
- uint32_t last_fn;
-
- /* cumulated values of current cell from SACCH dl */
- uint32_t frames;
- uint32_t snr;
- uint32_t berr;
- uint32_t rxlev;
-
- /* counters loss criterion */
- int16_t dsc, ds_fail;
- int16_t s, rl_fail;
-};
-
-enum {
- MS_SHUTDOWN_NONE = 0,
- MS_SHUTDOWN_IMSI_DETACH = 1,
- MS_SHUTDOWN_WAIT_RESET = 2,
- MS_SHUTDOWN_COMPL = 3,
-};
-
-/* One Mobilestation for osmocom */
-struct osmocom_ms {
- struct llist_head entity;
- char *name;
- struct osmo_wqueue l2_wq, sap_wq;
- uint16_t test_arfcn;
- struct osmol1_entity l1_entity;
-
- bool started, deleting;
- uint8_t shutdown;
- struct gsm_support support;
- struct gsm_settings settings;
- struct gsm_subscriber subscr;
- struct gsm_sim sim;
- struct lapdm_channel lapdm_channel;
- struct osmosap_entity sap_entity;
- struct rx_meas_stat meas;
- struct gsm48_rrlayer rrlayer;
- struct gsm322_plmn plmn;
- struct gsm322_cellsel cellsel;
- struct gsm48_mmlayer mmlayer;
- struct gsm48_cclayer cclayer;
- struct osmomncc_entity mncc_entity;
- struct llist_head trans_list;
-
- void *lua_state;
- int lua_cb_ref;
- char *lua_script;
-};
+struct vty;
enum osmobb_sig_subsys {
SS_L1CTL,
SS_GLOBAL,
+ SS_L23_VTY,
+ SS_L23_SUBSCR,
+ SS_L23_TRANS,
};
enum osmobb_l1ctl_sig {
@@ -120,6 +30,43 @@ enum osmobb_global_sig {
S_GLOBAL_SHUTDOWN,
};
+enum osmobb_l23_vty_sig {
+ S_L23_VTY_MS_START,
+ S_L23_VTY_MS_STOP,
+};
+
+enum osmobb_l23_subscriber {
+ S_L23_SUBSCR_SIM_ATTACHED,
+ S_L23_SUBSCR_SIM_DETACHED,
+ S_L23_SUBSCR_SIM_AUTH_RESP,
+};
+
+enum osmobb_l23_trans_sig {
+ S_L23_CC_TRANS_ALLOC, /* new transaction has been allocated */
+ S_L23_CC_TRANS_FREE, /* transaction is about to be free()d */
+ S_L23_CC_TRANS_STATE_CHG, /* transaction state has been changed */
+};
+
+struct osmobb_l23_vty_sig_data {
+ struct vty *vty;
+ union {
+ struct {
+ struct osmocom_ms *ms;
+ int rc; /* CMD_SUCCESS/CMD_WARNING */
+ } ms_start;
+ struct {
+ struct osmocom_ms *ms;
+ bool force;
+ int rc; /* CMD_SUCCESS/CMD_WARNING */
+ } ms_stop;
+ };
+};
+
+struct osmobb_l23_subscr_sim_auth_resp_sig_data {
+ struct osmocom_ms *ms;
+ uint8_t sres[4];
+};
+
struct osmobb_fbsb_res {
struct osmocom_ms *ms;
int8_t snr;
@@ -142,6 +89,7 @@ struct osmobb_tch_mode_conf {
struct osmocom_ms *ms;
uint8_t tch_mode;
uint8_t audio_mode;
+ uint8_t tch_flags;
};
struct osmobb_neigh_pm_ind {
@@ -149,5 +97,3 @@ struct osmobb_neigh_pm_ind {
uint16_t band_arfcn;
uint8_t rx_lev;
};
-
-#endif
diff --git a/src/host/layer23/include/osmocom/bb/common/settings.h b/src/host/layer23/include/osmocom/bb/common/settings.h
new file mode 100644
index 00000000..1d3e53df
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/settings.h
@@ -0,0 +1,348 @@
+#ifndef _settings_h
+#define _settings_h
+
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include <osmocom/bb/common/sim.h>
+
+struct osmocom_ms;
+struct osmobb_apn;
+
+#define MOB_C7_DEFLT_ANY_TIMEOUT 30
+
+/* CC (Call Control) message handling entity */
+enum mncc_handler_t {
+ /* Built-in mobile's MNCC */
+ MNCC_HANDLER_INTERNAL,
+ /* External MNCC application via UNIX-socket */
+ MNCC_HANDLER_EXTERNAL,
+ /* No call support */
+ MNCC_HANDLER_DUMMY,
+};
+
+/* TCH I/O handler for voice calls */
+enum tch_voice_io_handler {
+ /* No handler, drop frames */
+ TCH_VOICE_IOH_NONE = 0,
+ /* libosmo-gapk based handler */
+ TCH_VOICE_IOH_GAPK,
+ /* L1 PHY (e.g. Calypso DSP) */
+ TCH_VOICE_IOH_L1PHY,
+ /* External MNCC app (via MNCC socket) */
+ TCH_VOICE_IOH_MNCC_SOCK,
+ /* Return to sender */
+ TCH_VOICE_IOH_LOOPBACK,
+};
+
+extern const struct value_string tch_voice_io_handler_names[];
+static inline const char *tch_voice_io_handler_name(enum tch_voice_io_handler val)
+{ return get_value_string(tch_voice_io_handler_names, val); }
+
+/* TCH I/O handler for data calls */
+enum tch_data_io_handler {
+ /* No handler, drop frames */
+ TCH_DATA_IOH_NONE = 0,
+ /* UNIX socket */
+ TCH_DATA_IOH_UNIX_SOCK,
+ /* Return to sender */
+ TCH_DATA_IOH_LOOPBACK,
+};
+
+extern const struct value_string tch_data_io_handler_names[];
+static inline const char *tch_data_io_handler_name(enum tch_data_io_handler val)
+{ return get_value_string(tch_data_io_handler_names, val); }
+
+/* TCH I/O format for voice calls */
+enum tch_voice_io_format {
+ /* RFC3551 for FR/EFR, RFC5993 for HR, RFC4867 for AMR */
+ TCH_VOICE_IOF_RTP,
+ /* Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx) */
+ TCH_VOICE_IOF_TI,
+};
+
+extern const struct value_string tch_voice_io_format_names[];
+static inline const char *tch_voice_io_format_name(enum tch_voice_io_format val)
+{ return get_value_string(tch_voice_io_format_names, val); }
+
+/* TCH I/O format for data calls */
+enum tch_data_io_format {
+ /* Osmocom format, used by trxcon and virtphy */
+ TCH_DATA_IOF_OSMO,
+ /* Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx) */
+ TCH_DATA_IOF_TI,
+};
+
+extern const struct value_string tch_data_io_format_names[];
+static inline const char *tch_data_io_format_name(enum tch_data_io_format val)
+{ return get_value_string(tch_data_io_format_names, val); }
+
+struct tch_voice_settings {
+ enum tch_voice_io_handler io_handler;
+ enum tch_voice_io_format io_format;
+ char alsa_output_dev[128];
+ char alsa_input_dev[128];
+};
+
+struct tch_data_settings {
+ enum tch_data_io_handler io_handler;
+ enum tch_data_io_format io_format;
+ char unix_socket_path[128];
+};
+
+struct test_sim_settings {
+ char imsi[OSMO_IMSI_BUF_SIZE];
+ uint32_t tmsi;
+ uint8_t ki_type;
+ uint8_t ki[16]; /* 128 bit max */
+ bool barr;
+ bool rplmn_valid;
+ struct osmo_plmn_id rplmn;
+ uint16_t lac;
+ bool imsi_attached;
+ bool always_search_hplmn;
+ struct {
+ bool valid;
+ uint32_t ptmsi; /* invalid tmsi: GSM_RESERVED_TMSI */
+ uint32_t ptmsi_sig; /* P-TMSI signature, 3 bytes */
+ struct gprs_ra_id rai;
+ enum gsm1111_ef_locigprs_rau_status gu_state; /* GU, TS 24.008 */
+ bool imsi_attached;
+ } locigprs;
+};
+
+/* Data (CSD) call type and rate, values like in the '<speed>' part of 'AT+CBST'.
+ * See 3GPP TS 27.007, section 6.7 "Select bearer service type +CBST". */
+enum data_call_type_rate {
+ DATA_CALL_TR_AUTO = 0,
+ DATA_CALL_TR_V21_300 = 1,
+ DATA_CALL_TR_V22_1200 = 2,
+ DATA_CALL_TR_V23_1200_75 = 3,
+ DATA_CALL_TR_V22bis_2400 = 4,
+ DATA_CALL_TR_V26ter_2400 = 5,
+ DATA_CALL_TR_V32_4800 = 6,
+ DATA_CALL_TR_V32_9600 = 7,
+ DATA_CALL_TR_V34_9600 = 12,
+ DATA_CALL_TR_V34_14400 = 14,
+ DATA_CALL_TR_V34_19200 = 15,
+ DATA_CALL_TR_V34_28800 = 16,
+ DATA_CALL_TR_V34_33600 = 17,
+ DATA_CALL_TR_V120_1200 = 34,
+ DATA_CALL_TR_V120_2400 = 36,
+ DATA_CALL_TR_V120_4800 = 38,
+ DATA_CALL_TR_V120_9600 = 39,
+ DATA_CALL_TR_V120_14400 = 43,
+ DATA_CALL_TR_V120_19200 = 47,
+ DATA_CALL_TR_V120_28800 = 48,
+ DATA_CALL_TR_V120_38400 = 49,
+ DATA_CALL_TR_V120_48000 = 50,
+ DATA_CALL_TR_V120_56000 = 51,
+ DATA_CALL_TR_V110_300 = 65,
+ DATA_CALL_TR_V110_1200 = 66,
+ DATA_CALL_TR_V110_2400 = 68,
+ DATA_CALL_TR_V110_4800 = 70,
+ DATA_CALL_TR_V110_9600 = 71,
+ DATA_CALL_TR_V110_14400 = 75,
+ DATA_CALL_TR_V110_19200 = 79,
+ DATA_CALL_TR_V110_28800 = 80,
+ DATA_CALL_TR_V110_38400 = 81,
+ DATA_CALL_TR_V110_48000 = 82,
+ DATA_CALL_TR_V110_56000 = 83,
+ DATA_CALL_TR_V110_64000 = 84,
+ DATA_CALL_TR_BTR_56000 = 115,
+ DATA_CALL_TR_BTR_64000 = 116,
+ DATA_CALL_TR_PIAFS32k_32000 = 120,
+ DATA_CALL_TR_PIAFS64k_64000 = 121,
+ DATA_CALL_TR_MMEDIA_28800 = 130,
+ DATA_CALL_TR_MMEDIA_32000 = 131,
+ DATA_CALL_TR_MMEDIA_33600 = 132,
+ DATA_CALL_TR_MMEDIA_56000 = 133,
+ DATA_CALL_TR_MMEDIA_64000 = 134,
+};
+
+/* Data (CSD) call parameters */
+struct data_call_params {
+ enum data_call_type_rate type_rate;
+ enum gsm48_bcap_transp transp;
+
+ /* async call parameters */
+ bool is_async;
+ unsigned int nr_stop_bits;
+ unsigned int nr_data_bits;
+ enum gsm48_bcap_parity parity;
+};
+
+struct gsm_settings {
+ char layer2_socket_path[128];
+ char sap_socket_path[128];
+ char mncc_socket_path[128];
+
+ /* MNCC handler */
+ enum mncc_handler_t mncc_handler;
+
+ /* TCH settings */
+ struct tch_voice_settings tch_voice;
+ struct tch_data_settings tch_data;
+
+ /* IMEI */
+ char imei[GSM23003_IMEI_NUM_DIGITS + 1];
+ char imeisv[GSM23003_IMEISV_NUM_DIGITS + 1];
+ char imei_random;
+
+ /* network search */
+ int plmn_mode; /* PLMN_MODE_* */
+
+ /* SIM */
+ int sim_type; /* enum gsm_subscriber_sim_type,
+ * selects card on power on */
+ char emergency_imsi[OSMO_IMSI_BUF_SIZE];
+
+ /* SMS */
+ char sms_sca[22];
+ bool store_sms;
+
+ /* test card simulator settings */
+ struct test_sim_settings test_sim;
+
+ /* call related settings */
+ uint8_t cw; /* set if call-waiting is allowed */
+ uint8_t auto_answer;
+ uint8_t clip, clir;
+ uint8_t half, half_prefer;
+
+ /* changing default behavior */
+ uint8_t alter_tx_power;
+ uint8_t alter_tx_power_value;
+ int8_t alter_delay;
+ uint8_t stick;
+ uint16_t stick_arfcn;
+ uint8_t skip_max_per_band;
+ uint8_t no_lupd;
+ uint8_t no_neighbour;
+
+ /* supported by configuration */
+ uint8_t cc_dtmf;
+ uint8_t sms_ptp;
+ uint8_t a5_1;
+ uint8_t a5_2;
+ uint8_t a5_3;
+ uint8_t a5_4;
+ uint8_t a5_5;
+ uint8_t a5_6;
+ uint8_t a5_7;
+ uint8_t p_gsm;
+ uint8_t e_gsm;
+ uint8_t r_gsm;
+ uint8_t dcs;
+ uint8_t gsm_850;
+ uint8_t pcs;
+ uint8_t gsm_480;
+ uint8_t gsm_450;
+ uint8_t class_900;
+ uint8_t class_dcs;
+ uint8_t class_850;
+ uint8_t class_pcs;
+ uint8_t class_400;
+ uint8_t freq_map[128+38];
+ uint8_t full_v1;
+ uint8_t full_v2;
+ uint8_t full_v3;
+ uint8_t half_v1;
+ uint8_t half_v3;
+ uint8_t ch_cap; /* channel capability */
+ int8_t min_rxlev_dbm; /* min dBm to access */
+
+ /* CSD modes */
+ bool csd_tch_f144;
+ bool csd_tch_f96;
+ bool csd_tch_f48;
+ bool csd_tch_h48;
+ bool csd_tch_f24;
+ bool csd_tch_h24;
+
+ /* support for ASCI */
+ bool vgcs; /* support of VGCS */
+ bool vbs; /* support of VBS */
+
+ /* radio */
+ uint16_t dsc_max;
+ uint8_t force_rekey;
+
+ /* dialing */
+ struct llist_head abbrev;
+
+ /* EDGE / UMTS / CDMA */
+ uint8_t edge_ms_sup;
+ uint8_t edge_psk_sup;
+ uint8_t edge_psk_uplink;
+ uint8_t class_900_edge;
+ uint8_t class_dcs_pcs_edge;
+ uint8_t umts_fdd;
+ uint8_t umts_tdd;
+ uint8_t cdma_2000;
+ uint8_t dtm;
+ uint8_t class_dtm;
+ uint8_t dtm_mac;
+ uint8_t dtm_egprs;
+
+ /* Timeout for GSM 03.22 C7 state */
+ uint8_t any_timeout;
+
+ /* ASCI settings */
+ bool uplink_release_local;
+ bool asci_allow_any;
+
+ /* call parameters */
+ struct {
+ struct data_call_params data;
+ } call_params;
+};
+
+struct gsm_settings_abbrev {
+ struct llist_head list;
+ char abbrev[4];
+ char number[32];
+ char name[32];
+};
+
+int gsm_settings_arfcn(struct osmocom_ms *ms);
+int gsm_settings_init(struct osmocom_ms *ms);
+int gsm_settings_exit(struct osmocom_ms *ms);
+char *gsm_check_imei(const char *imei, const char *sv);
+int gsm_random_imei(struct gsm_settings *set);
+
+struct gprs_settings {
+ struct llist_head apn_list;
+
+ /* RFC1144 TCP/IP header compression */
+ struct {
+ int active;
+ int passive;
+ int s01;
+ } pcomp_rfc1144;
+
+ /* V.42vis data compression */
+ struct {
+ int active;
+ int passive;
+ int p0;
+ int p1;
+ int p2;
+ } dcomp_v42bis;
+};
+
+int gprs_settings_init(struct osmocom_ms *ms);
+int gprs_settings_fi(struct osmocom_ms *ms);
+struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char *apn_name);
+int ms_dispatch_all_apn(struct osmocom_ms *ms, uint32_t event, void *data);
+
+extern char *layer2_socket_path;
+
+#endif /* _settings_h */
+
diff --git a/src/host/layer23/include/osmocom/bb/common/sim.h b/src/host/layer23/include/osmocom/bb/common/sim.h
index 8b1f830c..8aae1fc8 100644
--- a/src/host/layer23/include/osmocom/bb/common/sim.h
+++ b/src/host/layer23/include/osmocom/bb/common/sim.h
@@ -13,12 +13,14 @@
* 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.
- *
*/
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/core/endian.h>
+
+struct osmocom_ms;
/* 9.2 commands */
#define GSM1111_CLASS_GSM 0xa0
@@ -212,6 +214,7 @@ struct gsm1111_response_mfdf {
} __attribute__ ((packed));
struct gsm1111_response_mfdf_gsm {
+#if OSMO_IS_LITTLE_ENDIAN
uint8_t file_char;
uint8_t num_df;
uint8_t num_ef;
@@ -230,10 +233,24 @@ struct gsm1111_response_mfdf_gsm {
rfu5:3,
unblk2_init:1;
uint8_t more_data[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t file_char;
+ uint8_t num_df;
+ uint8_t num_ef;
+ uint8_t num_codes;
+ uint8_t rfu1;
+ uint8_t chv1_init:1, rfu2:3, chv1_remain:4;
+ uint8_t unblk1_init:1, rfu3:3, unblk1_remain:4;
+ uint8_t chv2_init:1, rfu4:3, chv2_remain:4;
+ uint8_t unblk2_init:1, rfu5:3, unblk2_remain:4;
+ uint8_t more_data[0];
+#endif
} __attribute__ ((packed));
/* Section 9.2.1 (response to selecting EF) */
struct gsm1111_response_ef {
+#if OSMO_IS_LITTLE_ENDIAN
uint16_t rfu1;
uint16_t file_size;
uint16_t file_id;
@@ -251,6 +268,20 @@ struct gsm1111_response_ef {
rfu4:5;
uint8_t length;
uint8_t structure;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint16_t rfu1;
+ uint16_t file_size;
+ uint16_t file_id;
+ uint8_t tof;
+ uint8_t inc_allowed;
+ uint8_t acc_read:4, acc_update:4;
+ uint8_t acc_inc:4, rfu2:4;
+ uint8_t acc_reha:4, acc_inval:4;
+ uint8_t rfu4:5, ru_inval:1, rfu3:1, not_inval:1;
+ uint8_t length;
+ uint8_t structure;
+#endif
} __attribute__ ((packed));
/* Section 10.3.17 */
@@ -258,9 +289,34 @@ struct gsm1111_ef_loci {
uint32_t tmsi;
struct gsm48_loc_area_id lai;
uint8_t tmsi_time;
- uint8_t lupd_status;
+ uint8_t lupd_status; /* enum gsm1111_ef_loci_lupd_status */
} __attribute__ ((packed));
+enum gsm1111_ef_loci_lupd_status {
+ GSM1111_EF_LOCI_LUPD_ST_UPDATED = 0,
+ GSM1111_EF_LOCI_LUPD_ST_NOT_UPDATED = 1,
+ GSM1111_EF_LOCI_LUPD_ST_PLMN_NOT_ALLOWED = 2,
+ GSM1111_EF_LOCI_LUPD_ST_LA_NOT_ALLOWED = 3,
+ GSM1111_EF_LOCI_LUPD_ST_RESERVED = 7,
+};
+
+/* Section 10.3.33 */
+struct gsm1111_ef_locigprs {
+ uint32_t ptmsi;
+ uint16_t ptmsi_sig_hi;
+ uint8_t ptmsi_sig_lo;
+ struct gsm48_ra_id rai;
+ uint8_t rau_status; /* enum gsm1111_ef_locigprs_rau_status */
+} __attribute__ ((packed));
+
+enum gsm1111_ef_locigprs_rau_status {
+ GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED = 0,
+ GSM1111_EF_LOCIGPRS_RAU_ST_NOT_UPDATED = 1,
+ GSM1111_EF_LOCIGPRS_RAU_ST_PLMN_NOT_ALLOWED = 2,
+ GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED = 3,
+ GSM1111_EF_LOCIGPRS_RAU_ST_RESERVED = 7,
+};
+
/* Section 10.5.1 */
struct gsm1111_ef_adn {
uint8_t len_bcd;
diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/common/subscriber.h
index 698b0fdc..d36b4c6c 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
+++ b/src/host/layer23/include/osmocom/bb/common/subscriber.h
@@ -1,29 +1,54 @@
#ifndef _SUBSCRIBER_H
#define _SUBSCRIBER_H
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/gsm/gsm48.h>
+
/* GSM 04.08 4.1.2.2 SIM update status */
-#define GSM_SIM_U0_NULL 0
-#define GSM_SIM_U1_UPDATED 1
-#define GSM_SIM_U2_NOT_UPDATED 2
-#define GSM_SIM_U3_ROAMING_NA 3
+enum gsm_sub_sim_ustate {
+ GSM_SIM_U0_NULL,
+ GSM_SIM_U1_UPDATED,
+ GSM_SIM_U2_NOT_UPDATED,
+ GSM_SIM_U3_ROAMING_NA,
+};
+extern const struct value_string gsm_sub_sim_ustate_names[];
+static inline const char *gsm_sub_sim_ustate_name(enum gsm_sub_sim_ustate val)
+{
+ return get_value_string(gsm_sub_sim_ustate_names, val);
+}
+
+/* 3GPP TS 24.008 4.1.3.2 GPRS update status */
+enum gsm_sub_sim_gustate {
+ GSM_SIM_GU0_NULL,
+ GSM_SIM_GU1_UPDATED,
+ GSM_SIM_GU2_NOT_UPDATED,
+ GSM_SIM_GU3_ROAMING_NA,
+};
+extern const struct value_string gsm_sub_sim_gustate_names[];
+static inline const char *gsm_sub_sim_gustate_name(enum gsm_sub_sim_gustate val)
+{
+ return get_value_string(gsm_sub_sim_gustate_names, val);
+}
struct gsm_sub_plmn_list {
struct llist_head entry;
- uint16_t mcc, mnc;
+ struct osmo_plmn_id plmn;
};
struct gsm_sub_plmn_na {
struct llist_head entry;
- uint16_t mcc, mnc;
+ struct osmo_plmn_id plmn;
uint8_t cause;
};
-#define GSM_IMSI_LENGTH 16
-
#define GSM_SIM_IS_READER(type) \
(type == GSM_SIM_TYPE_L1PHY || type == GSM_SIM_TYPE_SAP)
-enum {
+enum gsm_subscriber_sim_type {
GSM_SIM_TYPE_NONE = 0,
GSM_SIM_TYPE_L1PHY,
GSM_SIM_TYPE_TEST,
@@ -34,19 +59,19 @@ struct gsm_subscriber {
struct osmocom_ms *ms;
/* status */
- uint8_t sim_type; /* type of sim */
- uint8_t sim_valid; /* sim inserted and valid */
- uint8_t ustate; /* update status */
- uint8_t imsi_attached; /* attached state */
+ enum gsm_subscriber_sim_type sim_type; /* type of sim */
+ bool sim_valid; /* sim inserted and valid */
+ enum gsm_sub_sim_ustate ustate; /* update status */
+ bool imsi_attached; /* attached state */
/* IMSI & co */
- char imsi[GSM_IMSI_LENGTH];
+ char imsi[OSMO_IMSI_BUF_SIZE];
char msisdn[31]; /* may include access codes */
char iccid[21]; /* 20 + termination */
/* TMSI / LAI */
- uint32_t tmsi; /* invalid tmsi: 0xffffffff */
- uint16_t mcc, mnc, lac; /* invalid lac: 0x0000 */
+ uint32_t tmsi; /* invalid tmsi: GSM_RESERVED_TMSI */
+ struct osmo_location_area_id lai; /* invalid lac: 0x0000 */
/* key */
@@ -59,7 +84,7 @@ struct gsm_subscriber {
uint8_t t6m_hplmn; /* timer for hplmn search */
/* special things */
- uint8_t always_search_hplmn;
+ bool always_search_hplmn;
/* search hplmn in other countries also (for test cards) */
uint8_t any_timeout;
/* timer to restart 'any cell selection' */
@@ -67,16 +92,16 @@ struct gsm_subscriber {
char sim_spn[17]; /* name of service privider */
/* PLMN last registered */
- uint8_t plmn_valid;
- uint16_t plmn_mcc, plmn_mnc;
+ bool plmn_valid;
+ struct osmo_plmn_id plmn;
/* our access */
- uint8_t acc_barr; /* if we may access, if cell barred */
+ bool acc_barr; /* if we may access, if cell barred */
uint16_t acc_class; /* bitmask of what we may access */
/* talk to SIM */
uint8_t sim_state;
- uint8_t sim_pin_required; /* state: wait for PIN */
+ bool sim_pin_required; /* state: wait for PIN */
uint8_t sim_file_index;
uint32_t sim_handle_query;
uint32_t sim_handle_update;
@@ -84,35 +109,38 @@ struct gsm_subscriber {
/* SMS */
char sms_sca[22];
+
+ struct {
+ uint8_t gu_state; /* GU, TS 24.008 */
+ bool rai_valid;
+ struct gprs_ra_id rai;
+ uint32_t ptmsi; /* invalid tmsi: GSM_RESERVED_TMSI */
+ uint32_t ptmsi_sig; /* P-TMSI signature, 3 bytes */
+ bool imsi_attached;
+ } gprs;
};
int gsm_subscr_init(struct osmocom_ms *ms);
int gsm_subscr_exit(struct osmocom_ms *ms);
-int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
- uint16_t lac, uint32_t tmsi, uint8_t imsi_attached);
+int gsm_subscr_insert(struct osmocom_ms *ms);
+int gsm_subscr_remove(struct osmocom_ms *ms);
+
int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code,
uint8_t res_type, uint16_t param_len, const uint8_t *param_val);
-int gsm_subscr_sapcard(struct osmocom_ms *ms);
-int gsm_subscr_remove_sapcard(struct osmocom_ms *ms);
-int gsm_subscr_simcard(struct osmocom_ms *ms);
-void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2,
- int8_t mode);
+int gsm_subscr_sim_pin(struct osmocom_ms *ms, const char *pin1, const char *pin2,
+ int8_t mode);
int gsm_subscr_write_loci(struct osmocom_ms *ms);
-int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq,
- uint8_t *rand, uint8_t no_sim);
-int gsm_subscr_remove(struct osmocom_ms *ms);
+int gsm_subscr_write_locigprs(struct osmocom_ms *ms);
+int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, const uint8_t *rand,
+ bool no_sim);
void new_sim_ustate(struct gsm_subscriber *subscr, int state);
-int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
- uint16_t mnc);
-int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
- uint16_t mnc, uint8_t cause);
-int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
- uint16_t mnc);
+int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn);
+int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn, uint8_t cause);
+int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn);
int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms,
void (*print)(void *, const char *, ...), void *priv);
void gsm_subscr_dump(struct gsm_subscriber *subscr,
void (*print)(void *, const char *, ...), void *priv);
-char *gsm_check_imsi(const char *imsi);
int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr);
#endif /* _SUBSCRIBER_H */
diff --git a/src/host/layer23/include/osmocom/bb/mobile/support.h b/src/host/layer23/include/osmocom/bb/common/support.h
index c56c78e8..b0c71f5a 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/support.h
+++ b/src/host/layer23/include/osmocom/bb/common/support.h
@@ -91,6 +91,14 @@ struct gsm_support {
uint8_t half_v1;
uint8_t half_v3;
+ /* CSD modes */
+ uint8_t csd_tch_f144;
+ uint8_t csd_tch_f96;
+ uint8_t csd_tch_f48;
+ uint8_t csd_tch_h48;
+ uint8_t csd_tch_f24;
+ uint8_t csd_tch_h24;
+
/* EDGE / UMTS / CDMA */
uint8_t edge_ms_sup;
uint8_t edge_psk_sup;
@@ -110,7 +118,7 @@ struct gsm_support_scan_max {
uint16_t start;
uint16_t end;
uint16_t max;
- uint16_t temp;
+ uint16_t temp;
};
extern struct gsm_support_scan_max gsm_sup_smax[];
diff --git a/src/host/layer23/include/osmocom/bb/common/sysinfo.h b/src/host/layer23/include/osmocom/bb/common/sysinfo.h
index 7fea2daa..c31cf9d6 100644
--- a/src/host/layer23/include/osmocom/bb/common/sysinfo.h
+++ b/src/host/layer23/include/osmocom/bb/common/sysinfo.h
@@ -2,6 +2,7 @@
#define _SYSINFO_H
#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm23003.h>
/* collection of system information of the current cell */
@@ -17,11 +18,27 @@
#define FREQ_TYPE_REP_5bis 0x40 /* sub channel of SI 5bis */
#define FREQ_TYPE_REP_5ter 0x80 /* sub channel of SI 5ter */
+struct si10_cell_info {
+ uint8_t index; /* frequency index of the frequencies received in SI5* */
+ int16_t arfcn; /* ARFCN or -1 (if not found in SI5*) */
+ uint8_t bsic;
+ bool barred; /* Cell is barred, values below are invalid. */
+ bool la_different; /* Location area is different, so CRH is valid. */
+ uint8_t cell_resel_hyst_db;
+ uint8_t ms_txpwr_max_cch;
+ uint8_t rxlev_acc_min_db;
+ uint8_t cell_resel_offset;
+ uint8_t temp_offset;
+ uint8_t penalty_time;
+};
+
/* structure of all received system information */
struct gsm48_sysinfo {
/* flags of available information */
uint8_t si1, si2, si2bis, si2ter, si3,
- si4, si5, si5bis, si5ter, si6;
+ si4, si5, si5bis, si5ter, si6,
+ si13;
+ bool si10;
/* memory maps to simply detect change in system info messages */
uint8_t si1_msg[23];
@@ -34,6 +51,8 @@ struct gsm48_sysinfo {
uint8_t si5b_msg[18];
uint8_t si5t_msg[18];
uint8_t si6_msg[18];
+ uint8_t si10_msg[21];
+ uint8_t si13_msg[23];
struct gsm_sysinfo_freq freq[1024]; /* all frequencies */
uint16_t hopping[64]; /* hopping arfcn */
@@ -42,7 +61,7 @@ struct gsm48_sysinfo {
/* serving cell */
uint8_t bsic;
uint16_t cell_id;
- uint16_t mcc, mnc, lac; /* LAI */
+ struct osmo_location_area_id lai;
uint8_t max_retrans; /* decoded */
uint8_t tx_integer; /* decoded */
uint8_t reest_denied; /* 1 = denied */
@@ -52,7 +71,7 @@ struct gsm48_sysinfo {
/* si1 rest */
uint8_t nch;
uint8_t nch_position;
- uint8_t band_ind; /* set for DCS */
+ bool band_ind; /* set for DCS */
/* si3 rest */
uint8_t sp;
@@ -66,9 +85,46 @@ struct gsm48_sysinfo {
uint8_t ecsm;
uint8_t sched;
uint8_t sched_where;
- uint8_t gprs;
- uint8_t gprs_ra_colour;
- uint8_t gprs_si13_pos;
+
+ struct {
+ /* si3/si4 rest */
+ uint8_t supported;
+ uint8_t ra_colour;
+ uint8_t si13_pos;
+
+ /* si13 rest */
+ uint8_t hopping;
+ uint8_t hsn;
+ uint8_t rfl_num_len;
+ uint8_t rfl_num[4];
+
+ uint8_t ma_bitlen;
+ uint8_t ma_bitmap[64 / 8];
+ uint8_t arfcn_idx_len;
+ uint8_t arfcn_idx[16];
+
+ /* PBCCH is not present */
+ uint8_t rac;
+ uint8_t prio_acc_thresh;
+ uint8_t nco;
+
+ /* GPRS Cell Options */
+ uint8_t nmo;
+ uint8_t T3168;
+ uint8_t T3192;
+ uint8_t ab_type;
+ uint8_t ctrl_ack_type_use_block;
+ uint8_t bs_cv_max;
+ uint8_t pan_params_present;
+ uint8_t pan_dec;
+ uint8_t pan_inc;
+ uint8_t pan_max;
+
+ /* EGPRS Cell Options */
+ uint8_t egprs_supported;
+ uint8_t egprs_pkt_chan_req;
+ uint8_t egprs_bep_period;
+ } gprs;
/* cell selection */
int8_t ms_txpwr_max_cch;
@@ -118,45 +174,52 @@ struct gsm48_sysinfo {
uint8_t nb_reest_denied; /* 1 = denied */
uint8_t nb_cell_barr; /* 1 = barred */
uint16_t nb_class_barr; /* bit 10 is emergency */
+
+ /* SI 10 */
+ uint8_t si10_cell_num; /* number neighbor cells found in SI 10 */
+ struct si10_cell_info si10_cell[32]; /* 32 neighbor cell descriptions */
};
char *gsm_print_arfcn(uint16_t arfcn);
-uint8_t gsm_refer_pcs(uint16_t arfcn, struct gsm48_sysinfo *s);
-int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
- void (*print)(void *, const char *, ...), void *priv,
- uint8_t *freq_map);
+bool gsm_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s);
+uint16_t gsm_arfcn_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s, uint16_t arfcn);
+int gsm48_sysinfo_dump(const struct gsm48_sysinfo *s, uint16_t arfcn,
+ void (*print)(void *, const char *, ...),
+ void *priv, uint8_t *freq_map);
+int gsm48_si10_dump(const struct gsm48_sysinfo *s, void (*print)(void *, const char *, ...), void *priv);
int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
uint16_t *mnc, uint16_t *lac);
-int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc,
- uint16_t *arfcn);
-int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
- uint8_t *maio, uint8_t *hsn);
+int gsm48_decode_chan_h0(const struct gsm48_chan_desc *cd,
+ uint8_t *tsc, uint16_t *arfcn);
+int gsm48_decode_chan_h1(const struct gsm48_chan_desc *cd,
+ uint8_t *tsc, uint8_t *maio, uint8_t *hsn);
int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_1 *si, int len);
+ const struct gsm48_system_information_type_1 *si, int len);
int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_2 *si, int len);
+ const struct gsm48_system_information_type_2 *si, int len);
int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_2bis *si, int len);
+ const struct gsm48_system_information_type_2bis *si, int len);
int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_2ter *si, int len);
+ const struct gsm48_system_information_type_2ter *si, int len);
int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_3 *si, int len);
+ const struct gsm48_system_information_type_3 *si, int len);
int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_4 *si, int len);
+ const struct gsm48_system_information_type_4 *si, int len);
int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_5 *si, int len);
+ const struct gsm48_system_information_type_5 *si, int len);
int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_5bis *si, int len);
+ const struct gsm48_system_information_type_5bis *si, int len);
int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_5ter *si, int len);
+ const struct gsm48_system_information_type_5ter *si, int len);
int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_6 *si, int len);
+ const struct gsm48_system_information_type_6 *si, int len);
+int gsm48_decode_sysinfo10(struct gsm48_sysinfo *s,
+ const struct gsm48_system_information_type_10 *si, int len);
+int gsm48_decode_sysinfo13(struct gsm48_sysinfo *s,
+ const struct gsm48_system_information_type_13 *si, int len);
int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq,
- uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len,
- int si4);
-int gsm48_encode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t mcc,
- uint16_t mnc, uint16_t lac);
-int gsm48_decode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t *mcc,
- uint16_t *mnc, uint16_t *lac);
+ const uint8_t *ma, uint8_t len,
+ uint16_t *hopping, uint8_t *hopp_len, int si4);
+int16_t arfcn_from_freq_index(const struct gsm48_sysinfo *s, uint16_t index);
#endif /* _SYSINFO_H */
diff --git a/src/host/layer23/include/osmocom/bb/common/vty.h b/src/host/layer23/include/osmocom/bb/common/vty.h
new file mode 100644
index 00000000..b2a52c01
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/vty.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/core/signal.h>
+
+struct osmocom_ms;
+
+enum l23_vty_node {
+ MS_NODE = _LAST_OSMOVTY_NODE + 1,
+ TESTSIM_NODE,
+ GSMTAP_NODE,
+ _LAST_L23VTY_NODE,
+};
+
+int l23_vty_init(int (*config_write_ms_node_cb)(struct vty *), osmo_signal_cbfn *l23_vty_signal_cb);
+
+struct osmocom_ms *l23_vty_get_ms(const char *name, struct vty *vty);
+void l23_ms_dump(struct osmocom_ms *ms, struct vty *vty);
+void l23_vty_config_write_ms_node(struct vty *vty, const struct osmocom_ms *ms, const char *prefix);
+void l23_vty_config_write_ms_node_contents(struct vty *vty, const struct osmocom_ms *ms, const char *prefix);
+void l23_vty_config_write_ms_node_contents_final(struct vty *vty, const struct osmocom_ms *ms, const char *prefix);
+
+void l23_vty_ms_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+void l23_vty_printf(void *priv, const char *fmt, ...);
+
+extern bool l23_vty_reading;
+extern bool l23_vty_hide_default;
+
+extern struct llist_head ms_list;
+
+extern struct cmd_element l23_show_ms_cmd;
+extern struct cmd_element l23_cfg_ms_cmd;
diff --git a/src/host/layer23/include/osmocom/bb/misc/Makefile.am b/src/host/layer23/include/osmocom/bb/misc/Makefile.am
index 59760906..14d3af95 100644
--- a/src/host/layer23/include/osmocom/bb/misc/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/misc/Makefile.am
@@ -1 +1,8 @@
-noinst_HEADERS = layer3.h rslms.h cell_log.h
+noinst_HEADERS = \
+ cell_log.h \
+ geo.h \
+ layer3.h \
+ locate.h \
+ log.h \
+ rslms.h \
+ $(NULL)
diff --git a/src/host/layer23/include/osmocom/bb/misc/cell_log.h b/src/host/layer23/include/osmocom/bb/misc/cell_log.h
index bce066eb..bef6e6ee 100644
--- a/src/host/layer23/include/osmocom/bb/misc/cell_log.h
+++ b/src/host/layer23/include/osmocom/bb/misc/cell_log.h
@@ -14,10 +14,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.
- *
*/
int scan_init(struct osmocom_ms *_ms);
diff --git a/src/host/gsmmap/geo.h b/src/host/layer23/include/osmocom/bb/misc/geo.h
index 25e26cba..25e26cba 100644
--- a/src/host/gsmmap/geo.h
+++ b/src/host/layer23/include/osmocom/bb/misc/geo.h
diff --git a/src/host/gsmmap/locate.h b/src/host/layer23/include/osmocom/bb/misc/locate.h
index 26133452..26133452 100644
--- a/src/host/gsmmap/locate.h
+++ b/src/host/layer23/include/osmocom/bb/misc/locate.h
diff --git a/src/host/gsmmap/log.h b/src/host/layer23/include/osmocom/bb/misc/log.h
index d1520101..3183f876 100644
--- a/src/host/gsmmap/log.h
+++ b/src/host/layer23/include/osmocom/bb/misc/log.h
@@ -1,3 +1,6 @@
+#pragma once
+
+#include <osmocom/bb/common/sysinfo.h>
enum {
LOG_TYPE_NONE = 0,
@@ -26,6 +29,7 @@ struct node_mcc {
struct node_mnc {
struct node_mnc *next;
uint16_t mnc;
+ bool mnc_3_digits;
struct node_lac *lac;
};
@@ -72,7 +76,7 @@ struct node_meas {
};
struct node_mcc *get_node_mcc(uint16_t mcc);
-struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc);
+struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc, bool mnc_3_digits);
struct node_lac *get_node_lac(struct node_mnc *mnc, uint16_t lac);
struct node_cell *get_node_cell(struct node_lac *lac, uint16_t cellid);
struct node_meas *add_node_meas(struct node_cell *cell);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am
index 623964fd..5d2eeaf8 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am
@@ -1,4 +1,4 @@
noinst_HEADERS = gsm322.h gsm480_ss.h gsm411_sms.h gsm48_cc.h gsm48_mm.h \
- gsm48_rr.h mncc.h settings.h subscriber.h support.h \
- transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \
- app_mobile.h voice.h
+ gsm48_rr.h mncc.h gsm44068_gcc_bcc.h \
+ tch.h transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \
+ app_mobile.h gapk_io.h
diff --git a/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h
index 191f4baf..940dbce7 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h
@@ -8,10 +8,6 @@ extern char *config_dir;
struct osmocom_ms;
struct vty;
-int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
- const char *config_file);
-int l23_app_exit(void);
-int l23_app_work(int *quit);
int mobile_delete(struct osmocom_ms *ms, int force);
struct osmocom_ms *mobile_new(char *name);
int mobile_work(struct osmocom_ms *ms);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gapk_io.h b/src/host/layer23/include/osmocom/bb/mobile/gapk_io.h
new file mode 100644
index 00000000..ab8b6579
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/mobile/gapk_io.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#ifdef WITH_GAPK_IO
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/codecs.h>
+
+#define GAPK_ULDL_QUEUE_LIMIT 8
+
+/* Forward declarations */
+struct osmocom_ms;
+struct msgb;
+
+struct gapk_io_state {
+ /* src/alsa -> proc/codec -> sink/tch_fb */
+ struct osmo_gapk_pq *pq_source;
+ /* src/tch_fb -> proc/codec -> sink/alsa */
+ struct osmo_gapk_pq *pq_sink;
+
+ /* Description of currently used codec / format */
+ const struct osmo_gapk_format_desc *phy_fmt_desc;
+ const struct osmo_gapk_codec_desc *codec_desc;
+
+ /* DL TCH frame buffer (received, to be played) */
+ struct llist_head tch_dl_fb;
+ unsigned int tch_dl_fb_len;
+ /* UL TCH frame buffer (captured, to be sent) */
+ struct llist_head tch_ul_fb;
+ unsigned int tch_ul_fb_len;
+};
+
+struct gapk_io_state *
+gapk_io_state_alloc(struct osmocom_ms *ms,
+ enum osmo_gapk_codec_type codec);
+struct gapk_io_state *
+gapk_io_state_alloc_mode_rate(struct osmocom_ms *ms,
+ enum gsm48_chan_mode ch_mode,
+ bool full_rate);
+void gapk_io_state_free(struct gapk_io_state *state);
+
+void gapk_io_enqueue_dl(struct gapk_io_state *state, struct msgb *msg);
+void gapk_io_dequeue_ul(struct osmocom_ms *ms, struct gapk_io_state *state);
+
+#endif /* WITH_GAPK_IO */
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
index b332778d..4b20a1a1 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
@@ -1,10 +1,11 @@
#ifndef _GSM322_H
#define _GSM322_H
-#include <osmocom/bb/common/sysinfo.h>
-
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
+#include <osmocom/gsm/gsm23003.h>
+
+#include <osmocom/bb/common/sysinfo.h>
/* 4.3.1.1 List of states for PLMN selection process (automatic mode) */
#define GSM322_A0_NULL 0
@@ -42,7 +43,7 @@
/* GSM 03.22 events */
#define GSM322_EVENT_SWITCH_ON 1
-#define GSM322_EVENT_SWITCH_OFF 2
+#define GSM322_EVENT_SWITCH_OFF 2
#define GSM322_EVENT_SIM_INSERT 3
#define GSM322_EVENT_SIM_REMOVE 4
#define GSM322_EVENT_REG_SUCCESS 5
@@ -74,7 +75,7 @@ enum {
/* node for each PLMN */
struct gsm322_plmn_list {
struct llist_head entry;
- uint16_t mcc, mnc;
+ struct osmo_plmn_id plmn;
uint8_t rxlev; /* rx level in range format */
uint8_t cause; /* cause value, if PLMN is not allowed */
};
@@ -82,14 +83,14 @@ struct gsm322_plmn_list {
/* node for each forbidden LA */
struct gsm322_la_list {
struct llist_head entry;
- uint16_t mcc, mnc, lac;
+ struct osmo_location_area_id lai;
uint8_t cause;
};
/* node for each BA-List */
struct gsm322_ba_list {
struct llist_head entry;
- uint16_t mcc, mnc;
+ struct osmo_plmn_id plmn;
/* Band allocation for 1024+299 frequencies.
* First bit of first index is frequency 0.
*/
@@ -124,7 +125,7 @@ struct gsm322_plmn {
struct osmo_timer_list timer;
int plmn_curr; /* current index in sorted_plmn */
- uint16_t mcc, mnc; /* current network selected */
+ struct osmo_plmn_id plmn; /* current network selected */
};
/* state of CCCH activation */
@@ -171,7 +172,7 @@ struct gsm322_cellsel {
/* cell selection list per frequency. */
/* scan and tune state */
struct osmo_timer_list timer; /* cell selection timer */
- uint16_t mcc, mnc; /* current network to search for */
+ struct osmo_plmn_id plmn; /* current network to search for */
uint8_t powerscan; /* currently scanning for power */
uint8_t ccch_state; /* special state of current ccch */
uint32_t scan_state; /* special state of current scan */
@@ -188,7 +189,7 @@ struct gsm322_cellsel {
uint8_t selected; /* if a cell is selected */
uint16_t sel_arfcn; /* current selected serving cell! */
struct gsm48_sysinfo sel_si; /* copy of selected cell, will update */
- uint16_t sel_mcc, sel_mnc, sel_lac, sel_id;
+ struct osmo_cell_global_id sel_cgi;
/* cell re-selection */
struct llist_head nb_list; /* list of neighbour cells */
@@ -209,7 +210,7 @@ struct gsm322_cellsel {
/* GSM 03.22 message */
struct gsm322_msg {
int msg_type;
- uint16_t mcc, mnc;
+ struct osmo_plmn_id plmn;
uint8_t sysinfo; /* system information type */
uint8_t same_cell; /* select same cell when RET_IDLE */
uint8_t reject; /* location update reject cause */
@@ -229,18 +230,15 @@ int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg);
int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg);
int gsm322_plmn_dequeue(struct osmocom_ms *ms);
int gsm322_cs_dequeue(struct osmocom_ms *ms);
-int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
- uint16_t mnc, uint16_t lac, uint8_t cause);
-int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
- uint16_t mnc, uint16_t lac);
-int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
- uint16_t lac);
+int gsm322_add_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai, uint8_t cause);
+int gsm322_del_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai);
+int gsm322_is_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai);
int gsm322_dump_sorted_plmn(struct osmocom_ms *ms);
int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
void (*print)(void *, const char *, ...), void *priv);
int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
void (*print)(void *, const char *, ...), void *priv);
-int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
+int gsm322_dump_ba_list(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn,
void (*print)(void *, const char *, ...), void *priv);
int gsm322_dump_nb_list(struct gsm322_cellsel *cs,
void (*print)(void *, const char *, ...), void *priv);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h b/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h
new file mode 100644
index 00000000..e9889b32
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h
@@ -0,0 +1,43 @@
+/* VGCS/VBS call control */
+/*
+ * (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
+
+#define GSM44068_ALLOC_SIZE 2048
+#define GSM44068_ALLOC_HEADROOM 256
+
+static inline struct msgb *gsm44068_msgb_alloc_name(const char *name)
+{
+ return msgb_alloc_headroom(GSM44068_ALLOC_SIZE, GSM44068_ALLOC_HEADROOM, name);
+}
+
+int gsm44068_gcc_init(struct osmocom_ms *ms);
+int gsm44068_gcc_exit(struct osmocom_ms *ms);
+int gsm44068_rcv_gcc_bcc(struct osmocom_ms *ms, struct msgb *msg);
+int gsm44068_rcv_mm_idle(struct osmocom_ms *ms);
+struct gsm_trans *trans_find_ongoing_gcc_bcc(struct osmocom_ms *ms);
+int gcc_bcc_call(struct osmocom_ms *ms, uint8_t protocol, const char *number);
+int gcc_leave(struct osmocom_ms *ms);
+int gcc_bcc_hangup(struct osmocom_ms *ms);
+int gcc_talk(struct osmocom_ms *ms);
+int gcc_listen(struct osmocom_ms *ms);
+int gsm44068_dump_calls(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h
index bf3aa257..1f0db785 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h
@@ -1,11 +1,16 @@
#ifndef _GSM48_MM_H
#define _GSM48_MM_H
+struct gsm_settings;
+
/* GSM 04.07 9.2.2 */
#define GSM48_MMXX_MASK 0xf00
#define GSM48_MMCC_CLASS 0x100
#define GSM48_MMSS_CLASS 0x200
#define GSM48_MMSMS_CLASS 0x300
+#define GSM48_MMGCC_CLASS 0x500
+#define GSM48_MMBCC_CLASS 0x600
+#define GSM48_MMXX_REL_IND 0x022
#define GSM48_MMCC_EST_REQ 0x110
#define GSM48_MMCC_EST_IND 0x112
#define GSM48_MMCC_EST_CNF 0x111
@@ -49,18 +54,66 @@
#define GSM48_MMSMS_ERR_IND 0x372
#define GSM48_MMSMS_PROMPT_IND 0x382
#define GSM48_MMSMS_PROMPT_REJ 0x384
+/* MM messages for Voice Group/Broadcast Calls */
+#define GSM48_MMGCC_EST_REQ 0x510
+#define GSM48_MMGCC_EST_CNF 0x511
+#define GSM48_MMGCC_REL_REQ 0x520
+#define GSM48_MMGCC_REL_IND 0x522
+#define GSM48_MMGCC_DATA_REQ 0x530
+#define GSM48_MMGCC_DATA_IND 0x532
+#define GSM48_MMGCC_UNIT_DATA_REQ 0x540
+#define GSM48_MMGCC_UNIT_DATA_IND 0x542
+#define GSM48_MMGCC_REEST_REQ 0x560
+#define GSM48_MMGCC_REEST_CNF 0x561
+#define GSM48_MMGCC_ERR_IND 0x572
+#define GSM48_MMGCC_NOTIF_IND 0x582
+#define GSM48_MMGCC_GROUP_REQ 0x590
+#define GSM48_MMGCC_GROUP_CNF 0x591
+#define GSM48_MMGCC_UPLINK_REQ 0x5a0
+#define GSM48_MMGCC_UPLINK_CNF 0x5a1
+#define GSM48_MMGCC_UPLINK_REL_REQ 0x5a8
+#define GSM48_MMGCC_UPLINK_REL_IND 0x5aa
+#define GSM48_MMGCC_UPLINK_FREE_IND 0x5b2
+#define GSM48_MMGCC_UPLINK_BUSY_IND 0x5b6
+#define GSM48_MMBCC_EST_REQ 0x610
+#define GSM48_MMBCC_EST_CNF 0x611
+#define GSM48_MMBCC_REL_REQ 0x620
+#define GSM48_MMBCC_REL_IND 0x622
+#define GSM48_MMBCC_DATA_REQ 0x630
+#define GSM48_MMBCC_DATA_IND 0x632
+#define GSM48_MMBCC_UNIT_DATA_REQ 0x640
+#define GSM48_MMBCC_UNIT_DATA_IND 0x642
+#define GSM48_MMBCC_REEST_REQ 0x660
+#define GSM48_MMBCC_REEST_CNF 0x661
+#define GSM48_MMBCC_ERR_IND 0x672
+#define GSM48_MMBCC_NOTIF_IND 0x682
+#define GSM48_MMBCC_GROUP_REQ 0x690
+#define GSM48_MMBCC_GROUP_CNF 0x691
+#define GSM48_MMBCC_UPLINK_REQ 0x6a0
+#define GSM48_MMBCC_UPLINK_CNF 0x6a1
+#define GSM48_MMBCC_UPLINK_REL_REQ 0x6a8
+#define GSM48_MMBCC_UPLINK_REL_IND 0x6aa
+#define GSM48_MMBCC_UPLINK_FREE_IND 0x6b2
+#define GSM48_MMBCC_UPLINK_BUSY_IND 0x6b6
+
#define MMXX_ALLOC_SIZE 256
#define MMXX_ALLOC_HEADROOM 64
+#define MMXX_NOTIFY_SETUP 0
+#define MMXX_NOTIFY_RELEASE 1
+
/* MMxx-SAP header */
struct gsm48_mmxx_hdr {
- uint16_t msg_type; /* MMxx_* primitive */
- uint32_t ref; /* reference to transaction */
- uint32_t transaction_id; /* transaction identifier */
- uint8_t sapi; /* sapi */
- uint8_t emergency; /* emergency type of call */
- uint8_t cause; /* cause used for release */
+ uint16_t msg_type; /* MMxx_* primitive */
+ uint32_t ref; /* reference to transaction */
+ uint32_t transaction_id; /* transaction identifier */
+ uint8_t sapi; /* sapi */
+ uint8_t emergency; /* emergency type of call */
+ uint8_t cause; /* cause used for release */
+ uint8_t notify; /* notify ongoing ASCI call */
+ bool ch_desc_present; /* notifies channel */
+ struct gsm48_chan_desc ch_desc; /* group channel */
} __attribute__((packed));
/* GSM 6.1.2 */
@@ -133,12 +186,25 @@ struct gsm48_mmr {
#define GSM48_MM_EVENT_SYSINFO 14
#define GSM48_MM_EVENT_USER_PLMN_SEL 15
#define GSM48_MM_EVENT_LOST_COVERAGE 16
+#define GSM48_MM_EVENT_NOTIFICATION 17
+#define GSM48_MM_EVENT_UPLINK_FREE 18
+#define GSM48_MM_EVENT_UPLINK_BUSY 19
/* message for MM events */
struct gsm48_mm_event {
- uint32_t msg_type;
+ uint32_t msg_type;
- uint8_t sres[4];
+ union {
+ /* GSM48_MM_EVENT_AUTH_RESPONSE */
+ uint8_t sres[4];
+ /* GSM48_MM_EVENT_NOTIFICATION */
+ struct {
+ uint8_t gcr[5];
+ bool ch_desc_present;
+ struct gsm48_chan_desc ch_desc;
+ bool gone;
+ } __attribute__((packed)) notification;
+ };
} __attribute__((packed));
/* GSM 04.08 MM timers */
@@ -184,7 +250,7 @@ struct gsm48_mmlayer {
uint8_t lupd_rej_cause; /* cause of last reject */
uint8_t lupd_periodic; /* periodic update pending */
uint8_t lupd_retry; /* pending T3211/T3213 to */
- uint16_t lupd_mcc, lupd_mnc, lupd_lac;
+ struct osmo_location_area_id lupd_lai;
/* imsi detach */
uint8_t delay_detach; /* do detach when possible */
@@ -196,6 +262,14 @@ struct gsm48_mmlayer {
/* sapi 3 */
int sapi3_link;
+
+ /* VGCS additional states */
+ struct {
+ bool enabled; /* We are in group/broadcast mode. */
+ bool group_call; /* This is a group call, not a broadcast call. */
+ uint32_t callref; /* Callref of this call. */
+ bool normal_service; /* Service state before group transmit mode. */
+ } vgcs;
};
/* MM connection entry */
@@ -203,7 +277,7 @@ struct gsm48_mm_conn {
struct llist_head list;
struct gsm48_mmlayer *mm;
- /* ref and type form a unique tuple */
+ /* ref and protocol form a unique tuple */
uint32_t ref; /* reference to trans */
uint8_t protocol;
uint8_t transaction_id;
@@ -212,6 +286,8 @@ struct gsm48_mm_conn {
int state;
};
+int gsm48_encode_mi_lv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi);
+int gsm48_encode_mi_tlv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi);
uint8_t gsm48_current_pwr_lev(struct gsm_settings *set, uint16_t arfcn);
int gsm48_mm_init(struct osmocom_ms *ms);
int gsm48_mm_exit(struct osmocom_ms *ms);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
index 9b499a6b..112e85a3 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
@@ -1,6 +1,7 @@
#ifndef _GSM48_RR_H
#define _GSM48_RR_H
+#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#define GSM_TA_CM 55385
@@ -9,6 +10,10 @@
#define T200_DCCH_SHARED 2 /* SDCCH shares SAPI 0 and 3 */
#define T200_ACCH 2 /* SACCH SAPI 3 */
+/* GSM 04.08 RR timers */
+#define GSM_T3128_MS 1, 0 /* Uplink investigation timer. */
+#define GSM_T3130_MS 5, 0 /* Uplink access timeout. */
+
/* GSM 04.07 9.1.2 */
#define GSM48_RR_EST_REQ 0x10
@@ -22,6 +27,15 @@
#define GSM48_RR_ABORT_REQ 0x60
#define GSM48_RR_ABORT_IND 0x62
#define GSM48_RR_ACT_REQ 0x70
+/* These are non-stadard primitives, used for group receive/transmit modes. */
+#define GSM48_RR_GROUP_REQ 0x80 /* Join a group channel in group receive mode. */
+#define GSM48_RR_GROUP_CNF 0x81 /* Group channel has been joined. */
+#define GSM48_RR_GROUP_REL_REQ 0x84 /* Release group channel. */
+#define GSM48_RR_GROUP_REL_IND 0x86 /* Group channel has been released or failed. */
+#define GSM48_RR_UPLINK_REQ 0x90 /* Request uplink for group transmit mode. */
+#define GSM48_RR_UPLINK_CNF 0x91 /* Access granted. */
+#define GSM48_RR_UPLINK_REL_REQ 0x94 /* Release uplink for group receive mode. */
+#define GSM48_RR_UPLINK_REL_IND 0x96 /* Access denied or failed or uplink released. */
#define RR_EST_CAUSE_EMERGENCY 1
#define RR_EST_CAUSE_REESTAB_TCH_F 2
@@ -44,6 +58,8 @@
#define RR_REL_CAUSE_EMERGENCY_ONLY 6
#define RR_REL_CAUSE_LOST_SIGNAL 7
#define RR_REL_CAUSE_LINK_FAILURE 8
+#define RR_REL_CAUSE_UPLINK_BUSY 9
+#define RR_REL_CAUSE_UPLINK_REJECTED 10
#define RR_SYNC_CAUSE_CIPHERING 1
@@ -69,6 +85,13 @@ struct gsm48_rr_hdr {
#define GSM48_RR_ST_DEDICATED 2
#define GSM48_RR_ST_REL_PEND 3
+/* group states (VGCS) */
+enum gsm48_rr_gstate {
+ GSM48_RR_GST_OFF = 0,
+ GSM48_RR_GST_RECEIVE,
+ GSM48_RR_GST_TRANSMIT,
+};
+
/* special states for SAPI 3 link */
#define GSM48_RR_SAPI3ST_IDLE 0
#define GSM48_RR_SAPI3ST_WAIT_EST 1
@@ -100,6 +123,7 @@ struct gsm48_rr_cd {
uint8_t start; /* start time available */
struct gsm_time start_tm; /* start time */
uint8_t mode; /* mode of channel */
+ uint8_t tch_flags; /* E.g. L1CTL_TCH_FLAG_RXONLY */
uint8_t cipher; /* ciphering of channel */
};
@@ -149,7 +173,7 @@ struct gsm48_rrlayer {
/* channel request states */
uint8_t wait_assign; /* waiting for assignment state */
uint8_t n_chan_req; /* number left, incl. current */
- uint8_t chan_req_val; /* current request value */
+ uint8_t chan_req_val; /* current request value */
uint8_t chan_req_mask; /* mask of random bits */
/* state of dedicated mdoe */
@@ -157,7 +181,7 @@ struct gsm48_rrlayer {
/* cr_hist */
uint8_t cr_ra; /* stores requested ra until confirmed */
- struct gsm48_cr_hist cr_hist[3];
+ struct gsm48_cr_hist cr_hist[5];
/* V(SD) sequence numbers */
uint16_t v_sd; /* 16 PD 1-bit sequence numbers packed */
@@ -194,6 +218,21 @@ struct gsm48_rrlayer {
/* sapi 3 */
uint8_t sapi3_state;
uint8_t sapi3_link_id;
+
+ /* group call */
+ struct {
+ struct llist_head notif_list; /* list of received call notifications */
+ enum gsm48_rr_gstate group_state; /* extension to RR state for group transmit/receive modes */
+ struct gsm48_rr_cd cd_group; /* channel description of group call channel */
+ bool uplink_free; /* Is set, if uplink is currently free. */
+ uint8_t uic; /* UIC to use for access burst (-1 for BSIC) */
+ bool uplink_access; /* The network wants us to send listener access bursts. */
+ struct osmo_timer_list t_ul_free; /* Uplink free timer. (480ms timer) */
+ struct osmo_timer_list t3128; /* Uplink investigation timer. */
+ struct osmo_timer_list t3130; /* Uplink access timer. */
+ uint8_t uplink_tries; /* Counts number of tries to access the uplink. */
+ uint8_t uplink_counter; /* Counts number of access bursts per 'try'. */
+ } vgcs;
};
const char *get_rr_name(int value);
@@ -212,7 +251,7 @@ extern const char *gsm48_rr_state_names[];
int gsm48_rr_start_monitor(struct osmocom_ms *ms);
int gsm48_rr_stop_monitor(struct osmocom_ms *ms);
int gsm48_rr_alter_delay(struct osmocom_ms *ms);
-int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg);
+int gsm48_rr_tx_traffic(struct osmocom_ms *ms, struct msgb *msg);
int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode);
#endif /* _GSM48_RR_H */
diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc.h b/src/host/layer23/include/osmocom/bb/mobile/mncc.h
index 5e976cc2..d6facbbd 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/mncc.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/mncc.h
@@ -1,4 +1,4 @@
-/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
@@ -17,35 +17,12 @@
* 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.
- *
*/
-#ifndef _MNCC_H
-#define _MNCC_H
+#pragma once
-#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/mncc.h>
-struct gsm_call {
- struct llist_head entry;
-
- struct osmocom_ms *ms;
-
- uint32_t callref;
-
- bool init; /* call initiated, no response yet */
- bool hold; /* call on hold */
- bool ring; /* call ringing/knocking */
-
- struct osmo_timer_list dtmf_timer;
- uint8_t dtmf_state;
- uint8_t dtmf_index;
- char dtmf[32]; /* dtmf sequence */
-};
-
#define DTMF_ST_IDLE 0 /* no DTMF active */
#define DTMF_ST_START 1 /* DTMF started, waiting for resp. */
#define DTMF_ST_MARK 2 /* wait tone duration */
@@ -108,6 +85,9 @@ struct gsm_call {
#define GSM_TCHF_FRAME 0x0300
#define GSM_TCHF_FRAME_EFR 0x0301
+#define GSM_TCHH_FRAME 0x0302
+#define GSM_TCH_FRAME_AMR 0x0303
+#define GSM_BAD_FRAME 0x03ff
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@@ -128,6 +108,8 @@ struct gsm_call {
#define MNCC_F_KEYPAD 0x1000
#define MNCC_F_SIGNAL 0x2000
+struct osmocom_ms;
+
struct gsm_mncc {
/* context based information */
uint32_t msg_type;
@@ -174,6 +156,3 @@ struct gsm_data_frame {
const char *get_mncc_name(int value);
int mncc_recv(struct osmocom_ms *ms, int msg_type, void *arg);
void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
-
-#endif
-
diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h b/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h
index 49ce1a42..05c539b0 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h
@@ -1,6 +1,39 @@
#pragma once
-int mncc_call(struct osmocom_ms *ms, char *number);
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+struct osmocom_ms;
+
+enum gsm_call_type {
+ GSM_CALL_T_UNKNOWN = 0,
+ GSM_CALL_T_VOICE,
+ GSM_CALL_T_DATA, /* UDI or 3.1 kHz audio */
+ GSM_CALL_T_DATA_FAX,
+};
+
+struct gsm_call {
+ struct llist_head entry;
+
+ struct osmocom_ms *ms;
+
+ uint32_t callref;
+ enum gsm_call_type type;
+
+ bool init; /* call initiated, no response yet */
+ bool hold; /* call on hold */
+ bool ring; /* call ringing/knocking */
+
+ struct osmo_timer_list dtmf_timer;
+ uint8_t dtmf_state;
+ uint8_t dtmf_index;
+ char dtmf[32]; /* dtmf sequence */
+};
+
+int mncc_call(struct osmocom_ms *ms, const char *number,
+ enum gsm_call_type call_type);
int mncc_hangup(struct osmocom_ms *ms);
int mncc_answer(struct osmocom_ms *ms);
int mncc_hold(struct osmocom_ms *ms);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h
deleted file mode 100644
index 57f23ee5..00000000
--- a/src/host/layer23/include/osmocom/bb/mobile/settings.h
+++ /dev/null
@@ -1,144 +0,0 @@
-#ifndef _settings_h
-#define _settings_h
-
-#define MOB_C7_DEFLT_ANY_TIMEOUT 30
-
-/* TCH frame I/O handler */
-enum audio_io_handler {
- /* No handler, drop frames */
- AUDIO_IOH_NONE = 0,
- /* Return to sender */
- AUDIO_IOH_LOOPBACK,
-};
-
-extern const struct value_string audio_io_handler_names[];
-static inline const char *audio_io_handler_name(enum audio_io_handler val)
-{ return get_value_string(audio_io_handler_names, val); }
-
-struct audio_settings {
- enum audio_io_handler io_handler;
-};
-
-struct gsm_settings {
- char layer2_socket_path[128];
- char sap_socket_path[128];
-
- /* Audio settings */
- struct audio_settings audio;
-
- /* IMEI */
- char imei[16];
- char imeisv[17];
- char imei_random;
-
- /* network search */
- int plmn_mode; /* PLMN_MODE_* */
-
- /* SIM */
- int sim_type; /* selects card on power on */
- char emergency_imsi[16];
-
- /* SMS */
- char sms_sca[22];
- bool store_sms;
-
- /* test card simulator settings */
- char test_imsi[16];
- uint32_t test_tmsi;
- uint8_t test_ki_type;
- uint8_t test_ki[16]; /* 128 bit max */
- uint8_t test_barr;
- uint8_t test_rplmn_valid;
- uint16_t test_rplmn_mcc, test_rplmn_mnc;
- uint16_t test_lac;
- uint8_t test_imsi_attached;
- uint8_t test_always; /* ...search hplmn... */
-
- /* call related settings */
- uint8_t cw; /* set if call-waiting is allowed */
- uint8_t auto_answer;
- uint8_t clip, clir;
- uint8_t half, half_prefer;
-
- /* changing default behavior */
- uint8_t alter_tx_power;
- uint8_t alter_tx_power_value;
- int8_t alter_delay;
- uint8_t stick;
- uint16_t stick_arfcn;
- uint8_t skip_max_per_band;
- uint8_t no_lupd;
- uint8_t no_neighbour;
-
- /* supported by configuration */
- uint8_t cc_dtmf;
- uint8_t sms_ptp;
- uint8_t a5_1;
- uint8_t a5_2;
- uint8_t a5_3;
- uint8_t a5_4;
- uint8_t a5_5;
- uint8_t a5_6;
- uint8_t a5_7;
- uint8_t p_gsm;
- uint8_t e_gsm;
- uint8_t r_gsm;
- uint8_t dcs;
- uint8_t gsm_850;
- uint8_t pcs;
- uint8_t gsm_480;
- uint8_t gsm_450;
- uint8_t class_900;
- uint8_t class_dcs;
- uint8_t class_850;
- uint8_t class_pcs;
- uint8_t class_400;
- uint8_t freq_map[128+38];
- uint8_t full_v1;
- uint8_t full_v2;
- uint8_t full_v3;
- uint8_t half_v1;
- uint8_t half_v3;
- uint8_t ch_cap; /* channel capability */
- int8_t min_rxlev_dbm; /* min dBm to access */
-
- /* radio */
- uint16_t dsc_max;
- uint8_t force_rekey;
-
- /* dialing */
- struct llist_head abbrev;
-
- /* EDGE / UMTS / CDMA */
- uint8_t edge_ms_sup;
- uint8_t edge_psk_sup;
- uint8_t edge_psk_uplink;
- uint8_t class_900_edge;
- uint8_t class_dcs_pcs_edge;
- uint8_t umts_fdd;
- uint8_t umts_tdd;
- uint8_t cdma_2000;
- uint8_t dtm;
- uint8_t class_dtm;
- uint8_t dtm_mac;
- uint8_t dtm_egprs;
-
- /* Timeout for GSM 03.22 C7 state */
- uint8_t any_timeout;
-};
-
-struct gsm_settings_abbrev {
- struct llist_head list;
- char abbrev[4];
- char number[32];
- char name[32];
-};
-
-int gsm_settings_arfcn(struct osmocom_ms *ms);
-int gsm_settings_init(struct osmocom_ms *ms);
-int gsm_settings_exit(struct osmocom_ms *ms);
-char *gsm_check_imei(const char *imei, const char *sv);
-int gsm_random_imei(struct gsm_settings *set);
-
-#endif /* _settings_h */
-
diff --git a/src/host/layer23/include/osmocom/bb/mobile/tch.h b/src/host/layer23/include/osmocom/bb/mobile/tch.h
new file mode 100644
index 00000000..b026bd5d
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/mobile/tch.h
@@ -0,0 +1,28 @@
+#pragma once
+
+struct osmocom_ms;
+struct gsm_data_frame;
+struct msgb;
+
+struct tch_state {
+ bool rx_only; /* Rx TCH frames, but Tx nothing */
+ bool is_voice; /* voice (true) or data (false) */
+ union {
+ struct tch_voice_state {
+ enum tch_voice_io_handler handler;
+ struct gapk_io_state *gapk_io;
+ } voice;
+ struct tch_data_state {
+ enum tch_data_io_handler handler;
+ struct tch_csd_sock_state *sock;
+ struct osmo_v110_ta *v110_ta;
+ struct osmo_soft_uart *suart;
+ unsigned int num_tx;
+ uint8_t e1e2e3[3];
+ } data;
+ };
+};
+
+int tch_init(struct osmocom_ms *ms);
+int tch_send_msg(struct osmocom_ms *ms, struct msgb *msg);
+int tch_send_mncc_frame(struct osmocom_ms *ms, const struct gsm_data_frame *frame);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/transaction.h b/src/host/layer23/include/osmocom/bb/mobile/transaction.h
index 8c06d5d9..c1e94298 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/transaction.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/transaction.h
@@ -27,7 +27,6 @@ struct gsm_trans {
union {
struct {
-
/* current call state */
int state;
@@ -39,6 +38,7 @@ struct gsm_trans {
int T308_second; /* used to send release again */
struct osmo_timer_list timer;
struct gsm_mncc msg; /* stores setup/disconnect/release message */
+ struct gsm_mncc_bearer_cap *bcap;
} cc;
struct {
/* current supp.serv. state */
@@ -55,6 +55,26 @@ struct gsm_trans {
struct gsm_sms *sms;
} sms;
+ struct {
+ /* VGCS/VBS state machine */
+ struct osmo_fsm_inst *fi;
+
+ /* Call State (See Table 9.3 of TS 144.068) */
+ uint8_t call_state;
+
+ /* State attributes (See Table 9.7 of TS 144.068) */
+ uint8_t d_att, u_att, comm, orig;
+
+ /* Channel description last received via notification */
+ bool ch_desc_present;
+ struct gsm48_chan_desc ch_desc;
+
+ /* Flag to store termination request from upper layer. */
+ bool termination;
+
+ /* Flag to tell the state machine that call changes from separate link to group receive mode. */
+ bool receive_after_sl;
+ } gcc;
};
};
@@ -62,7 +82,7 @@ struct gsm_trans {
struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms,
uint8_t proto, uint8_t trans_id);
-struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms,
+struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, uint8_t protocol,
uint32_t callref);
struct gsm_trans *trans_alloc(struct osmocom_ms *ms,
diff --git a/src/host/layer23/include/osmocom/bb/mobile/voice.h b/src/host/layer23/include/osmocom/bb/mobile/voice.h
deleted file mode 100644
index a0524183..00000000
--- a/src/host/layer23/include/osmocom/bb/mobile/voice.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _voice_h
-#define _voice_h
-
-int gsm_voice_init(struct osmocom_ms *ms);
-int gsm_send_voice(struct osmocom_ms *ms, struct gsm_data_frame *data);
-
-#endif /* _voice_h */
diff --git a/src/host/layer23/include/osmocom/bb/mobile/vty.h b/src/host/layer23/include/osmocom/bb/mobile/vty.h
index d0668041..f03012e5 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/vty.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/vty.h
@@ -6,16 +6,16 @@
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/command.h>
+#include <osmocom/bb/common/vty.h>
+
enum ms_vty_node {
- MS_NODE = _LAST_OSMOVTY_NODE + 1,
- TESTSIM_NODE,
- SUPPORT_NODE,
- AUDIO_NODE,
+ SUPPORT_NODE = _LAST_L23VTY_NODE + 1,
+ TCH_VOICE_NODE,
+ TCH_DATA_NODE,
+ VGCS_NODE,
+ VBS_NODE,
};
-int ms_vty_go_parent(struct vty *vty);
int ms_vty_init(void);
-extern void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
-
#endif
diff --git a/src/host/layer23/include/osmocom/bb/modem/Makefile.am b/src/host/layer23/include/osmocom/bb/modem/Makefile.am
new file mode 100644
index 00000000..2f69d195
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/Makefile.am
@@ -0,0 +1,10 @@
+noinst_HEADERS = \
+ modem.h \
+ gmm.h \
+ grr.h \
+ llc.h \
+ rlcmac.h \
+ sm.h \
+ sndcp.h \
+ vty.h \
+ $(NULL)
diff --git a/src/host/layer23/include/osmocom/bb/modem/gmm.h b/src/host/layer23/include/osmocom/bb/modem/gmm.h
new file mode 100644
index 00000000..41e1e7e6
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/gmm.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct osmocom_ms;
+
+int modem_gmm_init(struct osmocom_ms *ms);
+
+int modem_gmm_gmmreg_attach_req(const struct osmocom_ms *ms);
+int modem_gmm_gmmreg_detach_req(const struct osmocom_ms *ms);
+int modem_gmm_gmmreg_sim_auth_rsp(const struct osmocom_ms *ms,
+ uint8_t *sres, uint8_t *kc, uint8_t kc_len);
diff --git a/src/host/layer23/include/osmocom/bb/modem/grr.h b/src/host/layer23/include/osmocom/bb/modem/grr.h
new file mode 100644
index 00000000..a86a089b
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/grr.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct msgb;
+struct osmocom_ms;
+struct lapdm_entity;
+struct osmo_fsm;
+
+enum grr_fsm_state {
+ GRR_ST_PACKET_NOT_READY,
+ GRR_ST_PACKET_IDLE,
+ GRR_ST_PACKET_ACCESS,
+ GRR_ST_PACKET_TRANSFER,
+};
+
+enum grr_fsm_event {
+ GRR_EV_BCCH_BLOCK_IND,
+ GRR_EV_PCH_AGCH_BLOCK_IND,
+ GRR_EV_CHAN_ACCESS_REQ,
+ GRR_EV_CHAN_ACCESS_CNF,
+ GRR_EV_PDCH_ESTABLISH_REQ,
+ GRR_EV_PDCH_RELEASE_REQ,
+ GRR_EV_PDCH_UL_TBF_CFG_REQ,
+ GRR_EV_PDCH_DL_TBF_CFG_REQ,
+ GRR_EV_PDCH_BLOCK_REQ,
+ GRR_EV_PDCH_BLOCK_CNF,
+ GRR_EV_PDCH_BLOCK_IND,
+ GRR_EV_PDCH_RTS_IND,
+};
+
+extern struct osmo_fsm grr_fsm_def;
+
+int modem_grr_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
diff --git a/src/host/layer23/include/osmocom/bb/modem/llc.h b/src/host/layer23/include/osmocom/bb/modem/llc.h
new file mode 100644
index 00000000..5fe5d49f
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/llc.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdbool.h>
+
+struct osmocom_ms;
+
+int modem_llc_init(struct osmocom_ms *ms, const char *cipher_plugin_path);
+
diff --git a/src/host/layer23/include/osmocom/bb/modem/modem.h b/src/host/layer23/include/osmocom/bb/modem/modem.h
new file mode 100644
index 00000000..7135bf71
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/modem.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <stdbool.h>
+
+int modem_start(void);
+int modem_gprs_attach_if_needed(struct osmocom_ms *ms);
+int modem_sync_to_cell(struct osmocom_ms *ms);
+
+enum modem_state {
+ MODEM_ST_IDLE,
+ MODEM_ST_ATTACHING,
+ MODEM_ST_ATTACHED
+};
+
+struct modem_app {
+ struct osmocom_ms *ms;
+ enum modem_state modem_state;
+};
+extern struct modem_app app_data;
diff --git a/src/host/layer23/include/osmocom/bb/modem/rlcmac.h b/src/host/layer23/include/osmocom/bb/modem/rlcmac.h
new file mode 100644
index 00000000..d1b054f8
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/rlcmac.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdbool.h>
+
+struct osmocom_ms;
+
+int modem_rlcmac_init(struct osmocom_ms *ms);
+
diff --git a/src/host/layer23/include/osmocom/bb/modem/sm.h b/src/host/layer23/include/osmocom/bb/modem/sm.h
new file mode 100644
index 00000000..b1a5df5a
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/sm.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdbool.h>
+
+struct osmocom_ms;
+
+int modem_sm_init(struct osmocom_ms *ms);
+int modem_sm_smreg_pdp_act_req(const struct osmocom_ms *ms, const struct osmobb_apn *apn);
diff --git a/src/host/layer23/include/osmocom/bb/modem/sndcp.h b/src/host/layer23/include/osmocom/bb/modem/sndcp.h
new file mode 100644
index 00000000..b2e6f0fd
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/sndcp.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stdbool.h>
+
+struct osmocom_ms;
+struct osmobb_apn;
+
+int modem_sndcp_init(struct osmocom_ms *ms);
+int modem_sndcp_sn_xid_req(struct osmobb_apn *apn);
+int modem_sndcp_sn_unitdata_req(struct osmobb_apn *apn, uint8_t *npdu, size_t npdu_len);
diff --git a/src/host/layer23/include/osmocom/bb/modem/vty.h b/src/host/layer23/include/osmocom/bb/modem/vty.h
new file mode 100644
index 00000000..5b69af60
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/modem/vty.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <osmocom/bb/common/vty.h>
+
+enum modem_vty_node {
+ APN_NODE = _LAST_L23VTY_NODE + 1,
+};
+
+int modem_vty_init(void);
+int modem_vty_go_parent(struct vty *vty);
diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am
index 58a5f7fb..3b6a4d8b 100644
--- a/src/host/layer23/src/Makefile.am
+++ b/src/host/layer23/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = common misc mobile
+SUBDIRS = common misc mobile modem
diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am
index a8d9f7e7..8f5aebaa 100644
--- a/src/host/layer23/src/common/Makefile.am
+++ b/src/host/layer23/src/common/Makefile.am
@@ -1,6 +1,37 @@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS)
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOGPRSRLCMAC_CFLAGS) \
+ $(LIBOSMOGPRSLLC_CFLAGS) \
+ $(LIBOSMOGPRSSNDCP_CFLAGS) \
+ $(LIBGPS_CFLAGS) \
+ $(NULL)
noinst_LIBRARIES = liblayer23.a
-liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_fsm.c sap_proto.c sap_interface.c \
- logging.c networks.c sim.c sysinfo.c gps.c l1ctl_lapdm_glue.c utils.c
+liblayer23_a_SOURCES = \
+ apn.c \
+ apn_fsm.c \
+ gps.c \
+ l1ctl.c \
+ l1l2_interface.c \
+ l1ctl_lapdm_glue.c \
+ logging.c \
+ ms.c \
+ networks.c \
+ sap_fsm.c \
+ sap_proto.c \
+ sap_interface.c \
+ settings.c \
+ sim.c \
+ subscriber.c \
+ support.c \
+ sysinfo.c \
+ utils.c \
+ vty.c \
+ $(NULL)
diff --git a/src/host/layer23/src/common/apn.c b/src/host/layer23/src/common/apn.c
new file mode 100644
index 00000000..cefa7641
--- /dev/null
+++ b/src/host/layer23/src/common/apn.c
@@ -0,0 +1,121 @@
+/*
+ * (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
+ * 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.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include <talloc.h>
+
+#include <osmocom/gprs/sm/sm.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/l23_app.h>
+
+struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name)
+{
+ struct osmobb_apn *apn;
+ apn = talloc_zero(ms, struct osmobb_apn);
+ if (!apn)
+ return NULL;
+
+ if (apn_fsm_ctx_init(&apn->fsm, apn) != 0)
+ goto ret_free;
+
+ talloc_set_name(apn, "apn_%s", name);
+ apn->cfg.name = talloc_strdup(apn, name);
+ apn->cfg.shutdown = true;
+ apn->cfg.tx_gpdu_seq = true;
+
+ apn->tun = osmo_tundev_alloc(apn, name);
+ if (!apn->tun)
+ goto ret_free_fsm;
+ osmo_tundev_set_priv_data(apn->tun, apn);
+
+ apn->ms = ms;
+ /* FIXME: may want to configure or pick free one in the future: */
+ apn->pdp.nsapi = 6;
+ apn->pdp.llc_sapi = OSMO_GPRS_SM_LLC_SAPI_SAPI3;
+
+ /* QoS zeroed to 14 bytes is a valid QoS seen sent by some phones. Use
+ * that as default for now. */
+ apn->pdp.qos_len = 14;
+
+ llist_add_tail(&apn->list, &ms->gprs.apn_list);
+ return apn;
+
+ret_free_fsm:
+ apn_fsm_ctx_release(&apn->fsm);
+ret_free:
+ talloc_free(apn);
+ return NULL;
+}
+
+void apn_free(struct osmobb_apn *apn)
+{
+ apn_fsm_ctx_release(&apn->fsm);
+ llist_del(&apn->list);
+ osmo_tundev_free(apn->tun);
+ talloc_free(apn);
+}
+
+int apn_start(struct osmobb_apn *apn)
+{
+ int rc;
+
+ if (apn->started)
+ return 0;
+
+ LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->cfg.dev_name);
+ /* Set TUN library callback. Must have been configured by the app: */
+ OSMO_ASSERT(l23_app_info.tun_data_ind_cb);
+ osmo_tundev_set_data_ind_cb(apn->tun, l23_app_info.tun_data_ind_cb);
+ osmo_tundev_set_dev_name(apn->tun, apn->cfg.dev_name);
+ osmo_tundev_set_netns_name(apn->tun, apn->cfg.dev_netns_name);
+
+ rc = osmo_tundev_open(apn->tun);
+ if (rc < 0) {
+ LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
+ return -1;
+ }
+
+ LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", osmo_tundev_get_dev_name(apn->tun));
+
+ LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n");
+ apn->started = true;
+ return 0;
+}
+
+int apn_stop(struct osmobb_apn *apn)
+{
+ LOGPAPN(LOGL_NOTICE, apn, "Stopping\n");
+
+ /* shutdown whatever old state might be left */
+ if (apn->tun) {
+ /* release tun device */
+ LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n",
+ osmo_tundev_get_dev_name(apn->tun));
+ osmo_tundev_close(apn->tun);
+ }
+
+ apn->started = false;
+ return 0;
+}
diff --git a/src/host/layer23/src/common/apn_fsm.c b/src/host/layer23/src/common/apn_fsm.c
new file mode 100644
index 00000000..2b523f1d
--- /dev/null
+++ b/src/host/layer23/src/common/apn_fsm.c
@@ -0,0 +1,244 @@
+/* Lifecycle of an APN */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * Author: Pau Espin Pedrol <pespin@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 <osmocom/core/tdef.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/apn_fsm.h>
+#include <osmocom/bb/common/apn.h>
+#define X(s) (1 << (s))
+
+static struct osmo_tdef T_defs_apn[] = {
+ { .T=1, .default_val=65, .desc = "Activating timeout (s)" },
+ { 0 } /* empty item at the end */
+};
+
+static const struct osmo_tdef_state_timeout apn_fsm_timeouts[32] = {
+ [APN_ST_DISABLED] = {},
+ [APN_ST_INACTIVE] = {},
+ [APN_ST_ACTIVATING] = { .T=1 },
+ [APN_ST_ACTIVE] = {},
+};
+
+#define apn_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, apn_fsm_timeouts, T_defs_apn, -1)
+
+static void st_apn_disabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv;
+
+ apn_stop(ctx->apn);
+}
+
+static void st_apn_disabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case APN_EV_GPRS_ALLOWED:
+ if (*((bool *)data) == true)
+ apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_apn_inactive_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv;
+
+ int rc = apn_start(ctx->apn);
+ if (rc < 0)
+ apn_fsm_state_chg(fi, APN_ST_DISABLED);
+
+ /* FIXME: Here once we find a way to store whether the ms object is GMM
+ attached, we can transition directly to ACTIVATING. */
+}
+
+static void st_apn_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case APN_EV_GPRS_ALLOWED:
+ if (*((bool *)data) == false)
+ apn_fsm_state_chg(fi, APN_ST_DISABLED);
+ break;
+ case APN_EV_GMM_ATTACHED:
+ apn_fsm_state_chg(fi, APN_ST_ACTIVATING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_apn_activating_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ /* FIXME: We could send SMREG-PDP_ACT.req from here. Right now that's done by the app. */
+}
+
+static void st_apn_activating(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case APN_EV_GPRS_ALLOWED:
+ /* TODO: Tx PDP DEACT ACC */
+ apn_fsm_state_chg(fi, APN_ST_DISABLED);
+ break;
+ case APN_EV_GMM_DETACHED:
+ apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+ break;
+ case APN_EV_RX_SM_ACT_PDP_CTX_REJ:
+ apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+ break;
+ case APN_EV_RX_SM_ACT_PDP_CTX_ACC:
+ apn_fsm_state_chg(fi, APN_ST_ACTIVE);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_apn_active_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv;
+ struct osmo_netdev *netdev;
+
+ netdev = osmo_tundev_get_netdev(ctx->apn->tun);
+ osmo_netdev_ifupdown(netdev, true);
+}
+
+static void st_apn_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case APN_EV_GPRS_ALLOWED:
+ /* TODO: Tx PDP DEACT ACC */
+ apn_fsm_state_chg(fi, APN_ST_DISABLED);
+ break;
+ case APN_EV_GMM_DETACHED:
+ apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+ break;
+ case APN_EV_RX_SM_DEACT_PDP_CTX_ACC:
+ apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int apn_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ switch (fi->T) {
+ case 1:
+ apn_fsm_state_chg(fi, APN_ST_INACTIVE);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return 0;
+}
+
+static struct osmo_fsm_state apn_fsm_states[] = {
+ [APN_ST_DISABLED] = {
+ .in_event_mask =
+ X(APN_EV_GPRS_ALLOWED),
+ .out_state_mask =
+ X(APN_ST_INACTIVE),
+ .name = "DISABLED",
+ .onenter = st_apn_disabled_on_enter,
+ .action = st_apn_disabled,
+ },
+ [APN_ST_INACTIVE] = {
+ .in_event_mask =
+ X(APN_EV_GPRS_ALLOWED) |
+ X(APN_EV_GMM_ATTACHED),
+ .out_state_mask =
+ X(APN_ST_ACTIVATING),
+ .name = "INACTIVE",
+ .onenter = st_apn_inactive_on_enter,
+ .action = st_apn_inactive,
+ },
+ [APN_ST_ACTIVATING] = {
+ .in_event_mask =
+ X(APN_EV_GPRS_ALLOWED) |
+ X(APN_EV_GMM_DETACHED) |
+ X(APN_EV_RX_SM_ACT_PDP_CTX_REJ) |
+ X(APN_EV_RX_SM_ACT_PDP_CTX_ACC),
+ .out_state_mask =
+ X(APN_ST_DISABLED) |
+ X(APN_ST_INACTIVE) |
+ X(APN_ST_ACTIVE),
+ .name = "ACTIVATING",
+ .onenter = st_apn_activating_on_enter,
+ .action = st_apn_activating,
+ },
+ [APN_ST_ACTIVE] = {
+ .in_event_mask =
+ X(APN_EV_GPRS_ALLOWED) |
+ X(APN_EV_GMM_DETACHED)|
+ X(APN_EV_RX_SM_DEACT_PDP_CTX_ACC),
+ .out_state_mask =
+ X(APN_ST_DISABLED) |
+ X(APN_ST_INACTIVE),
+ .name = "ACTIVE",
+ .onenter = st_apn_active_on_enter,
+ .action = st_apn_active,
+ },
+};
+
+const struct value_string apn_fsm_event_names[] = {
+ { APN_EV_GPRS_ALLOWED, "GPRS_ALLOWED" },
+ { APN_EV_GMM_ATTACHED, "GMM_ATTACHED" },
+ { APN_EV_GMM_DETACHED, "GMM_DETACHED" },
+ { APN_EV_RX_SM_ACT_PDP_CTX_REJ, "ACT_PDP_CTX_REJ" },
+ { APN_EV_RX_SM_ACT_PDP_CTX_ACC, "ACT_PDP_CTX_ACC" },
+ { APN_EV_RX_SM_DEACT_PDP_CTX_ACC, "DEACT_PDP_CTX_ACC" },
+ { 0, NULL }
+};
+
+struct osmo_fsm apn_fsm = {
+ .name = "APN",
+ .states = apn_fsm_states,
+ .num_states = ARRAY_SIZE(apn_fsm_states),
+ .timer_cb = apn_fsm_timer_cb,
+ .event_names = apn_fsm_event_names,
+ .log_subsys = DTUN,
+};
+
+int apn_fsm_ctx_init(struct apn_fsm_ctx *ctx, struct osmobb_apn *apn)
+{
+ ctx->apn = apn;
+ ctx->fi = osmo_fsm_inst_alloc(&apn_fsm, apn, ctx, LOGL_INFO, NULL);
+ if (!ctx->fi)
+ return -ENODATA;
+
+ return 0;
+}
+
+void apn_fsm_ctx_release(struct apn_fsm_ctx *ctx)
+{
+ osmo_fsm_inst_free(ctx->fi);
+}
+
+static __attribute__((constructor)) void apn_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&apn_fsm) == 0);
+ osmo_tdefs_reset(T_defs_apn);
+}
diff --git a/src/host/layer23/src/common/gps.c b/src/host/layer23/src/common/gps.c
index 5225fe0e..3c69352c 100644
--- a/src/host/layer23/src/common/gps.c
+++ b/src/host/layer23/src/common/gps.c
@@ -13,10 +13,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>
@@ -33,6 +29,7 @@
#endif
#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/logging.h>
diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c
index da30767c..94979103 100644
--- a/src/host/layer23/src/common/l1ctl.c
+++ b/src/host/layer23/src/common/l1ctl.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>
@@ -41,44 +37,20 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/gsm/gsm0502.h>
#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1l2_interface.h>
-#include <osmocom/gsm/lapdm.h>
#include <osmocom/bb/common/logging.h>
-extern struct gsmtap_inst *gsmtap_inst;
-
-#define CB_FCCH -1
-#define CB_SCH -2
-#define CB_BCCH -3
-#define CB_IDLE -4
-
-/* according to TS 05.02 Clause 7 Table 3 of 9 an Figure 8a */
-static const int ccch_block_table[51] = {
- CB_FCCH, CB_SCH,/* 0..1 */
- CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */
- 0, 0, 0, 0, /* 6..9: B0 */
- CB_FCCH, CB_SCH,/* 10..11 */
- 1, 1, 1, 1, /* 12..15: B1 */
- 2, 2, 2, 2, /* 16..19: B2 */
- CB_FCCH, CB_SCH,/* 20..21 */
- 3, 3, 3, 3, /* 22..25: B3 */
- 4, 4, 4, 4, /* 26..29: B4 */
- CB_FCCH, CB_SCH,/* 30..31 */
- 5, 5, 5, 5, /* 32..35: B5 */
- 6, 6, 6, 6, /* 36..39: B6 */
- CB_FCCH, CB_SCH,/* 40..41 */
- 7, 7, 7, 7, /* 42..45: B7 */
- 8, 8, 8, 8, /* 46..49: B8 */
- -4 /* 50: Idle */
-};
-
/* determine the CCCH block number based on the frame number */
static unsigned int fn2ccch_block(uint32_t fn)
{
- int rc = ccch_block_table[fn%51];
+ int rc = gsm0502_fn2ccch_block(fn);
/* if FN is negative, we were called for something that's not CCCH! */
OSMO_ASSERT(rc >= 0);
return rc;
@@ -86,7 +58,7 @@ static unsigned int fn2ccch_block(uint32_t fn)
static uint8_t chantype_rsl2gsmtap_ext(uint8_t rsl_chantype, uint8_t link_id, uint32_t fn, uint8_t num_agch)
{
- uint8_t ret = chantype_rsl2gsmtap(rsl_chantype, link_id);
+ uint8_t ret = chantype_rsl2gsmtap2(rsl_chantype, link_id, false);
if (ret != GSMTAP_CHANNEL_PCH)
return ret;
@@ -233,7 +205,13 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
ccch = (struct l1ctl_data_ind *) msg->l2h;
gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
- rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
+ if (rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts) != 0) {
+ LOGP(DL1C, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, dl->chan_nr);
+ return -EINVAL;
+ }
+
DEBUGP(DL1C, "%s (%.4u/%.2u/%.2u) %d dBm: %s\n",
rsl_chan_nr_str(dl->chan_nr), tm.t1, tm.t2, tm.t3,
(int)dl->rx_level-110,
@@ -314,7 +292,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
* to clog up your logs */
if (!is_fill_frame(gsmtap_chan_type, ccch->data)) {
/* send CCCH data via GSMTAP */
- gsmtap_send(gsmtap_inst, ntohs(dl->band_arfcn), chan_ts,
+ gsmtap_send(l23_cfg.gsmtap.inst, ntohs(dl->band_arfcn), chan_ts,
gsmtap_chan_type, chan_ss, tm.fn, dl->rx_level-110,
dl->snr, ccch->data, sizeof(ccch->data));
}
@@ -339,6 +317,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
PRIM_OP_INDICATION, msg);
pp.u.data.chan_nr = dl->chan_nr;
pp.u.data.link_id = dl->link_id;
+ pp.u.data.fn = tm.fn;
/* send it up into LAPDm */
return lapdm_phsap_up(&pp.oph, le);
@@ -378,7 +357,6 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg,
struct l1ctl_hdr *l1h;
struct l1ctl_info_ul *l1i_ul;
uint8_t chan_type, chan_ts, chan_ss;
- uint8_t gsmtap_chan_type;
DEBUGP(DL1C, "(%s)\n", osmo_hexdump(msg->l2h, msgb_l2len(msg)));
@@ -390,11 +368,16 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg,
}
/* send copy via GSMTAP */
- rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts);
- gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id);
- gsmtap_send(gsmtap_inst, ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK,
- chan_ts, gsmtap_chan_type, chan_ss, 0, 127, 255,
- msg->l2h, msgb_l2len(msg));
+ if (rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts) == 0) {
+ uint8_t gsmtap_chan_type = chantype_rsl2gsmtap2(chan_type, link_id, false);
+ gsmtap_send(l23_cfg.gsmtap.inst, ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK,
+ chan_ts, gsmtap_chan_type, chan_ss, 0, 127, 0,
+ msg->l2h, msgb_l2len(msg));
+ } else {
+ LOGP(DL1C, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, chan_nr);
+ }
/* prepend uplink info header */
l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul));
@@ -460,8 +443,8 @@ int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode)
}
/* Transmit L1CTL_TCH_MODE_REQ */
-int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode,
- uint8_t audio_mode, uint8_t tch_loop_mode)
+int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags,
+ uint8_t tch_loop_mode)
{
struct msgb *msg;
struct l1ctl_tch_mode_req *req;
@@ -475,7 +458,9 @@ int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode,
req = (struct l1ctl_tch_mode_req *) msgb_put(msg, sizeof(*req));
req->tch_mode = tch_mode;
req->audio_mode = audio_mode;
+ req->tch_flags = tch_flags;
req->tch_loop_mode = tch_loop_mode;
+ /* TODO: Set AMR codec in req if req->tch_mode==GSM48_CMODE_SPEECH_AMR */
return osmo_send_l1(ms, msg);
}
@@ -527,8 +512,9 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr,
}
/* Transmit L1CTL_RACH_REQ */
-int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset,
- uint8_t combined)
+int l1ctl_tx_rach_req(struct osmocom_ms *ms,
+ uint8_t chan_nr, uint8_t link_id,
+ uint8_t ra, uint16_t offset, uint8_t combined, uint8_t uic)
{
struct msgb *msg;
struct l1ctl_info_ul *ul;
@@ -538,20 +524,22 @@ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset,
if (!msg)
return -1;
- DEBUGP(DL1C, "RACH Req. offset=%d combined=%d\n", offset, combined);
+ DEBUGP(DL1C, "RACH Req. offset=%d combined=%d uic=0x%02x\n", offset, combined, uic);
ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul));
+ ul->chan_nr = chan_nr;
+ ul->link_id = link_id;
req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req));
req->ra = ra;
req->offset = htons(offset);
req->combined = combined;
+ req->uic = uic;
return osmo_send_l1(ms, msg);
}
/* Transmit L1CTL_DM_EST_REQ */
-int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn,
- uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode,
- uint8_t audio_mode)
+int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode,
+ uint8_t audio_mode, uint8_t tch_flags)
{
struct msgb *msg;
struct l1ctl_info_ul *ul;
@@ -574,14 +562,13 @@ int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn,
req->h0.band_arfcn = htons(band_arfcn);
req->tch_mode = tch_mode;
req->audio_mode = audio_mode;
+ req->tch_flags = tch_flags;
return osmo_send_l1(ms, msg);
}
-int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn,
- uint16_t *ma, uint8_t ma_len,
- uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode,
- uint8_t audio_mode)
+int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, uint16_t *ma, uint8_t ma_len,
+ uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags)
{
struct msgb *msg;
struct l1ctl_info_ul *ul;
@@ -609,6 +596,7 @@ int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn,
req->h1.ma[i] = htons(ma[i]);
req->tch_mode = tch_mode;
req->audio_mode = audio_mode;
+ req->tch_flags = tch_flags;
return osmo_send_l1(ms, msg);
}
@@ -726,10 +714,7 @@ int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
/* just forward the SIM response to the SIM handler */
static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg)
{
- uint16_t len = msgb_l2len(msg);
- uint8_t *data = msg->data;
-
- LOGP(DL1C, LOGL_INFO, "SIM %s\n", osmo_hexdump(data, len));
+ LOGP(DL1C, LOGL_INFO, "SIM %s\n", msgb_hexdump_l1(msg));
sim_apdu_resp(ms, msg);
@@ -847,6 +832,7 @@ static int rx_l1_tch_mode_conf(struct osmocom_ms *ms, struct msgb *msg)
mc.tch_mode = conf->tch_mode;
mc.audio_mode = conf->audio_mode;
+ mc.tch_flags = conf->tch_flags;
mc.ms = ms;
osmo_signal_dispatch(SS_L1CTL, S_L1CTL_TCH_MODE_CONF, &mc);
@@ -858,7 +844,6 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg)
{
struct l1ctl_info_dl *dl;
struct l1ctl_traffic_ind *ti;
- size_t frame_len;
uint8_t *frame;
if (msgb_l1len(msg) < sizeof(*dl)) {
@@ -875,11 +860,8 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg)
msg->l2h = dl->payload;
msg->l3h = frame;
- /* Calculate the frame length */
- frame_len = msgb_l3len(msg);
-
- DEBUGP(DL1C, "TRAFFIC IND len=%zu (%s)\n", frame_len,
- osmo_hexdump(frame, frame_len));
+ LOGP(DL1C, LOGL_DEBUG, "Rx TRAFFIC.ind (fn=%u, chan_nr=0x%02x, len=%u): %s\n",
+ ntohl(dl->frame_nr), dl->chan_nr, msgb_l3len(msg), msgb_hexdump_l3(msg));
/* distribute or drop */
if (ms->l1_entity.l1_traffic_ind)
@@ -896,7 +878,6 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg,
struct l1ctl_hdr *l1h;
struct l1ctl_info_ul *l1i_ul;
struct l1ctl_traffic_req *tr;
- size_t frame_len;
uint8_t *frame;
/* Header handling */
@@ -904,13 +885,8 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg,
frame = (uint8_t *) tr->data;
msg->l3h = frame;
- /* Calculate the frame length */
- frame_len = msgb_l3len(msg);
-
- DEBUGP(DL1C, "TRAFFIC REQ len=%zu (%s)\n", frame_len,
- osmo_hexdump(frame, frame_len));
-
-// printf("TX %s\n", osmo_hexdump(frame, frame_len));
+ LOGP(DL1C, LOGL_DEBUG, "Tx TRAFFIC.req (chan_nr=0x%02x, len=%u): %s\n",
+ chan_nr, msgb_l3len(msg), msgb_hexdump_l3(msg));
/* prepend uplink info header */
l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul));
@@ -973,6 +949,190 @@ static int rx_l1_neigh_pm_ind(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
+/* Receive L1CTL_GPRS_UL_BLOCK_CNF */
+static int rx_l1_gprs_ul_block_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_gprs_ul_block_cnf *cnf = (void *)msg->l1h;
+
+ if (msgb_l1len(msg) < sizeof(*cnf)) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Rx malformed GPRS UL BLOCK.cnf (len=%u < %zu)\n",
+ msgb_l1len(msg), sizeof(*cnf));
+ return -EINVAL;
+ }
+ if (OSMO_UNLIKELY(cnf->tn >= 8)) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Rx malformed GPRS UL BLOCK.cnf (tn=%u)\n", cnf->tn);
+ return -EINVAL;
+ }
+
+ msg->l2h = (void *)&cnf->data[0];
+
+ DEBUGP(DL1C, "Rx GPRS UL BLOCK.cnf (fn=%u, tn=%u, len=%u): %s\n",
+ ntohl(cnf->fn), cnf->tn, msgb_l2len(msg), msgb_hexdump_l2(msg));
+
+ /* distribute or drop */
+ if (ms->l1_entity.l1_gprs_ul_block_cnf)
+ return ms->l1_entity.l1_gprs_ul_block_cnf(ms, msg);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/* Receive L1CTL_GPRS_DL_BLOCK_IND */
+static int rx_l1_gprs_dl_block_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_gprs_dl_block_ind *ind = (void *)msg->l1h;
+ uint8_t gsmtap_chan;
+ uint32_t fn;
+
+ if (msgb_l1len(msg) < sizeof(*ind)) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Rx malformed GPRS DL BLOCK.ind (len=%u < %zu)\n",
+ msgb_l1len(msg), sizeof(*ind));
+ return -EINVAL;
+ }
+ if (OSMO_UNLIKELY(ind->hdr.tn >= 8)) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Rx malformed GPRS DL BLOCK.ind (tn=%u)\n",
+ ind->hdr.tn);
+ return -EINVAL;
+ }
+
+ msg->l2h = (void *)&ind->data[0];
+
+ fn = ntohl(ind->hdr.fn);
+ if ((fn % 104) == 12)
+ gsmtap_chan = GSMTAP_CHANNEL_PTCCH;
+ else
+ gsmtap_chan = GSMTAP_CHANNEL_PDTCH;
+
+ gsmtap_send(l23_cfg.gsmtap.inst,
+ ms->rrlayer.cd_now.arfcn,
+ ind->hdr.tn, gsmtap_chan, 0, fn,
+ rxlev2dbm(ind->meas.rx_lev), 0,
+ msgb_l2(msg), msgb_l2len(msg));
+
+ DEBUGP(DL1C, "Rx GPRS DL BLOCK.ind (fn=%u, tn=%u, len=%u): %s\n",
+ fn, ind->hdr.tn, msgb_l2len(msg), msgb_hexdump_l2(msg));
+
+ /* distribute or drop */
+ if (ms->l1_entity.l1_gprs_dl_block_ind)
+ return ms->l1_entity.l1_gprs_dl_block_ind(ms, msg);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/* Receive L1CTL_GPRS_RTS_IND */
+static int rx_l1_gprs_rts_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_gprs_rts_ind *ind = (void *)msg->l1h;
+
+ if (msgb_l1len(msg) < sizeof(*ind)) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Rx malformed GPRS RTS.ind (len=%u < %zu)\n",
+ msgb_l1len(msg), sizeof(*ind));
+ return -EINVAL;
+ }
+ if (OSMO_UNLIKELY(ind->tn >= 8)) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Rx malformed GPRS RTS.ind (tn=%u)\n",
+ ind->tn);
+ return -EINVAL;
+ }
+
+ DEBUGP(DL1C, "Rx GPRS RTS.ind (fn=%u, tn=%u, usf=%u)\n",
+ ntohl(ind->fn), ind->tn, ind->usf);
+
+ /* distribute or drop */
+ if (ms->l1_entity.l1_gprs_rts_ind)
+ return ms->l1_entity.l1_gprs_rts_ind(ms, msg);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/* Transmit L1CTL_GPRS_UL_BLOCK_REQ */
+int l1ctl_tx_gprs_ul_block_req(struct osmocom_ms *ms, uint32_t fn, uint8_t tn,
+ const uint8_t *data, size_t data_len)
+{
+ struct l1ctl_gprs_ul_block_req *req;
+ struct msgb *msg;
+
+ msg = osmo_l1_alloc(L1CTL_GPRS_UL_BLOCK_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ req = (void *)msgb_put(msg, sizeof(*req));
+ req->hdr.fn = htonl(fn);
+ req->hdr.tn = tn;
+ if (data_len > 0)
+ memcpy(msgb_put(msg, data_len), data, data_len);
+
+ DEBUGP(DL1C, "Tx GPRS UL block (fn=%u, tn=%u, len=%zu): %s\n",
+ fn, tn, data_len, osmo_hexdump(data, data_len));
+
+ gsmtap_send(l23_cfg.gsmtap.inst,
+ ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK,
+ tn, GSMTAP_CHANNEL_PDTCH, 0, fn, 127, 0,
+ data, data_len);
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */
+int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref,
+ uint8_t slotmask, uint32_t start_fn)
+{
+ struct l1ctl_gprs_ul_tbf_cfg_req *req;
+ struct msgb *msg;
+
+ msg = osmo_l1_alloc(L1CTL_GPRS_UL_TBF_CFG_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ req = (void *)msgb_put(msg, sizeof(*req));
+ *req = (struct l1ctl_gprs_ul_tbf_cfg_req) {
+ .tbf_ref = tbf_ref,
+ .slotmask = slotmask,
+ .start_fn = htonl(start_fn),
+ };
+
+ DEBUGP(DL1C, "Tx GPRS UL TBF CFG: "
+ "tbf_ref=%u, slotmask=0x%02x, start_fn=%u\n",
+ tbf_ref, slotmask, start_fn);
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_GPRS_DL_TBF_CFG_REQ */
+int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref,
+ uint8_t slotmask, uint32_t start_fn,
+ uint8_t dl_tfi)
+{
+ struct l1ctl_gprs_dl_tbf_cfg_req *req;
+ struct msgb *msg;
+
+ msg = osmo_l1_alloc(L1CTL_GPRS_DL_TBF_CFG_REQ);
+ if (!msg)
+ return -ENOMEM;
+
+ req = (void *)msgb_put(msg, sizeof(*req));
+ *req = (struct l1ctl_gprs_dl_tbf_cfg_req) {
+ .tbf_ref = tbf_ref,
+ .slotmask = slotmask,
+ .start_fn = htonl(start_fn),
+ .dl_tfi = dl_tfi,
+ };
+
+ DEBUGP(DL1C, "Tx GPRS DL TBF CFG: "
+ "tbf_ref=%u, slotmask=0x%02x, start_fn=%u, dl_tfi=%u)\n",
+ tbf_ref, slotmask, start_fn, dl_tfi);
+
+ return osmo_send_l1(ms, msg);
+}
+
/* Receive incoming data from L1 using L1CTL format */
int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -1041,6 +1201,15 @@ int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg)
case L1CTL_TRAFFIC_CONF:
msgb_free(msg);
break;
+ case L1CTL_GPRS_UL_BLOCK_CNF:
+ rc = rx_l1_gprs_ul_block_cnf(ms, msg);
+ break;
+ case L1CTL_GPRS_DL_BLOCK_IND:
+ rc = rx_l1_gprs_dl_block_ind(ms, msg);
+ break;
+ case L1CTL_GPRS_RTS_IND:
+ rc = rx_l1_gprs_rts_ind(ms, msg);
+ break;
default:
LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", hdr->msg_type);
msgb_free(msg);
diff --git a/src/host/layer23/src/common/l1ctl_lapdm_glue.c b/src/host/layer23/src/common/l1ctl_lapdm_glue.c
index 0b2a8ed5..458cc81f 100644
--- a/src/host/layer23/src/common/l1ctl_lapdm_glue.c
+++ b/src/host/layer23/src/common/l1ctl_lapdm_glue.c
@@ -14,16 +14,14 @@
* 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 <stdint.h>
+#include <errno.h>
#include <l1ctl_proto.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/prim.h>
#include <osmocom/bb/common/l1ctl.h>
@@ -50,9 +48,12 @@ int l1ctl_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
case PRIM_PH_RACH:
l1ctl_tx_param_req(ms, pp->u.rach_req.ta,
pp->u.rach_req.tx_power);
- rc = l1ctl_tx_rach_req(ms, pp->u.rach_req.ra,
+ rc = l1ctl_tx_rach_req(ms,
+ RSL_CHAN_RACH, 0x00,
+ pp->u.rach_req.ra,
pp->u.rach_req.offset,
- pp->u.rach_req.is_combined_ccch);
+ pp->u.rach_req.is_combined_ccch,
+ 0xff);
break;
default:
rc = -EINVAL;
diff --git a/src/host/layer23/src/common/l1l2_interface.c b/src/host/layer23/src/common/l1l2_interface.c
index cd5f9106..8e270522 100644
--- a/src/host/layer23/src/common/l1l2_interface.c
+++ b/src/host/layer23/src/common/l1l2_interface.c
@@ -15,19 +15,17 @@
* 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 <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
+#include <osmocom/core/select.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -128,9 +126,9 @@ int layer2_close(struct osmocom_ms *ms)
if (ms->l2_wq.bfd.fd <= 0)
return -EINVAL;
+ osmo_fd_unregister(&ms->l2_wq.bfd);
close(ms->l2_wq.bfd.fd);
ms->l2_wq.bfd.fd = -1;
- osmo_fd_unregister(&ms->l2_wq.bfd);
osmo_wqueue_clear(&ms->l2_wq);
return 0;
diff --git a/src/host/layer23/src/common/logging.c b/src/host/layer23/src/common/logging.c
index ed799913..283b7f1a 100644
--- a/src/host/layer23/src/common/logging.c
+++ b/src/host/layer23/src/common/logging.c
@@ -14,10 +14,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.
- *
*/
@@ -36,7 +32,7 @@ static const struct log_info_cat default_categories[] = {
.name = "DCS",
.description = "Cell selection",
.color = "\033[34m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DNB] = {
.name = "DNB",
@@ -48,54 +44,66 @@ static const struct log_info_cat default_categories[] = {
.name = "DPLMN",
.description = "PLMN selection",
.color = "\033[32m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DRR] = {
.name = "DRR",
.description = "Radio Resource",
.color = "\033[1;34m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMM] = {
.name = "DMM",
.description = "Mobility Management",
.color = "\033[1;32m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DCC] = {
.name = "DCC",
.description = "Call Control",
.color = "\033[1;33m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DGCC] = {
+ .name = "DGCC",
+ .description = "Group Call Control",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DBCC] = {
+ .name = "DBCC",
+ .description = "Broadcast Call Control",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSS] = {
.name = "DSS",
.description = "Supplenmentary Services",
.color = "\033[1;35m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSMS] = {
.name = "DSMS",
.description = "Short Message Service",
.color = "\033[1;37m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMNCC] = {
.name = "DMNCC",
.description = "Mobile Network Call Control",
.color = "\033[1;37m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMEAS] = {
.name = "DMEAS",
.description = "MEasurement Reporting",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPAG] = {
.name = "DPAG",
.description = "Paging",
.color = "\033[33m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DL1C] = {
.name = "DL1C",
@@ -107,37 +115,37 @@ static const struct log_info_cat default_categories[] = {
.name = "DSAP",
.description = "SAP Control",
.color = "\033[1;31m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSUM] = {
.name = "DSUM",
.description = "Summary of Process",
.color = "\033[1;37m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSIM] = {
.name = "DSIM",
.description = "SIM client",
.color = "\033[0;35m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DGPS] = {
.name = "DGPS",
.description = "GPS",
.color = "\033[1;35m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMOB] = {
.name = "DMOB",
.description = "Mobile",
.color = "\033[1;35m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPRIM] = {
.name = "DPRIM",
.description = "PRIM",
.color = "\033[1;32m",
- .enabled = 1, .loglevel = LOGL_DEBUG,
+ .enabled = 1, .loglevel = LOGL_NOTICE,
},
[DLUA] = {
.name = "DLUA",
@@ -145,6 +153,54 @@ static const struct log_info_cat default_categories[] = {
.color = "\033[1;32m",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
+ [DGAPK] = {
+ .name = "DGAPK",
+ .description = "GAPK audio",
+ .color = "\033[0;36m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DCSD] = {
+ .name = "DCSD",
+ .description = "Circuit Switched Data",
+ .color = "\033[0;36m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DTUN] = {
+ .name = "DTUN",
+ .description = "Tunnel interface",
+ .color = "\033[0;37m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DRLCMAC] = {
+ .name = "DRLCMAC",
+ .description = "Radio Link Control / Medium Access Control (RLC/MAC)",
+ .color = "\033[0;38m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DLLC] = {
+ .name = "DLLC",
+ .description = "GPRS Logical Link Control Protocol (LLC)",
+ .color = "\033[0;39m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DSNDCP] = {
+ .name = "DSNDCP",
+ .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
+ .color = "\033[0;40m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DGMM] = {
+ .name = "DGMM",
+ .description = "GPRS Mobility Management (GMM)",
+ .color = "\033[0;32m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DSM] = {
+ .name = "DSM",
+ .description = "GPRS Session Management (SM)",
+ .color = "\033[0;31m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
const struct log_info log_info = {
diff --git a/src/host/layer23/src/common/main.c b/src/host/layer23/src/common/main.c
index 9d1c69ee..919a2315 100644
--- a/src/host/layer23/src/common/main.c
+++ b/src/host/layer23/src/common/main.c
@@ -15,27 +15,29 @@
* 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 <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/bb/common/sap_interface.h>
#include <osmocom/bb/misc/layer3.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/vty.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/application.h>
#include <osmocom/core/gsmtap_util.h>
-#include <osmocom/core/gsmtap.h>
#include <osmocom/core/utils.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/ports.h>
#include <arpa/inet.h>
@@ -47,77 +49,56 @@
#include <fcntl.h>
#include <signal.h>
-struct log_target *stderr_target;
+#include "config.h"
void *l23_ctx = NULL;
+struct l23_global_config l23_cfg;
-static char *layer2_socket_path = "/tmp/osmocom_l2";
static char *sap_socket_path = "/tmp/osmocom_sap";
struct llist_head ms_list;
-static struct osmocom_ms *ms = NULL;
static char *gsmtap_ip = NULL;
-static char *vty_ip = "127.0.0.1";
+static char *config_file = NULL;
+static char *log_cat_mask = NULL;
-unsigned short vty_port = 4247;
-int (*l23_app_work) (struct osmocom_ms *ms) = NULL;
-int (*l23_app_exit) (struct osmocom_ms *ms) = NULL;
+int (*l23_app_start)(void) = NULL;
+int (*l23_app_work)(void) = NULL;
+int (*l23_app_exit)(void) = NULL;
int quit = 0;
-struct gsmtap_inst *gsmtap_inst;
-
-const char *openbsc_copyright =
- "%s"
- "%s\n"
- "License GPLv2+: GNU GPL version 2 or later "
- "<http://gnu.org/licenses/gpl.html>\n"
- "This is free software: you are free to change and redistribute it.\n"
- "There is NO WARRANTY, to the extent permitted by law.\n\n";
static void print_usage(const char *app)
{
printf("Usage: %s\n", app);
}
-static void print_help()
+static void print_help(void)
{
- int options = 0xff;
- struct l23_app_info *app = l23_app_info();
-
- if (app && app->cfg_supported != 0)
- options = app->cfg_supported();
-
printf(" Some help...\n");
printf(" -h --help this text\n");
printf(" -s --socket /tmp/osmocom_l2. Path to the unix "
"domain socket (l2)\n");
- if (options & L23_OPT_SAP)
+ if (l23_app_info.opt_supported & L23_OPT_SAP)
printf(" -S --sap /tmp/osmocom_sap. Path to the "
"unix domain socket (BTSAP)\n");
- if (options & L23_OPT_ARFCN)
+ if (l23_app_info.opt_supported & L23_OPT_ARFCN)
printf(" -a --arfcn NR The ARFCN to be used for layer2.\n");
- if (options & L23_OPT_TAP)
+ if (l23_app_info.opt_supported & L23_OPT_TAP)
printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n");
- if (options & L23_OPT_VTY)
- printf(" -v --vty-port The VTY port number to telnet "
- "to. (default %u)\n", vty_port);
+ if (l23_app_info.opt_supported & L23_OPT_VTY)
+ printf(" -c --config-file The path to the VTY configuration file.\n");
- if (options & L23_OPT_DBG)
+ if (l23_app_info.opt_supported & L23_OPT_DBG)
printf(" -d --debug Change debug flags.\n");
- if (options & L23_OPT_VTYIP)
- printf(" -u --vty-ip The VTY IP to bind telnet to. "
- "(default %s)\n", vty_ip);
-
- if (app && app->cfg_print_help)
- app->cfg_print_help();
+ if (l23_app_info.cfg_print_help != NULL)
+ l23_app_info.cfg_print_help();
}
static void build_config(char **opt, struct option **option)
{
- struct l23_app_info *app;
struct option *app_opp = NULL;
int app_len = 0, len;
@@ -127,19 +108,17 @@ static void build_config(char **opt, struct option **option)
{"sap", 1, 0, 'S'},
{"arfcn", 1, 0, 'a'},
{"gsmtap-ip", 1, 0, 'i'},
- {"vty-ip", 1, 0, 'u'},
- {"vty-port", 1, 0, 'v'},
+ {"config-file", 1, 0, 'c'},
{"debug", 1, 0, 'd'},
};
- app = l23_app_info();
- *opt = talloc_asprintf(l23_ctx, "hs:S:a:i:v:d:u:%s",
- app && app->getopt_string ? app->getopt_string : "");
+ *opt = talloc_asprintf(l23_ctx, "hs:S:a:i:c:d:%s",
+ l23_app_info.getopt_string ? l23_app_info.getopt_string : "");
len = ARRAY_SIZE(long_options);
- if (app && app->cfg_getopt_opt)
- app_len = app->cfg_getopt_opt(&app_opp);
+ if (l23_app_info.cfg_getopt_opt != NULL)
+ app_len = l23_app_info.cfg_getopt_opt(&app_opp);
*option = talloc_zero_array(l23_ctx, struct option, len + app_len + 1);
memcpy(*option, long_options, sizeof(long_options));
@@ -149,7 +128,6 @@ static void build_config(char **opt, struct option **option)
static void handle_options(int argc, char **argv)
{
- struct l23_app_info *app = l23_app_info();
struct option *long_options;
char *opt;
@@ -170,29 +148,26 @@ static void handle_options(int argc, char **argv)
exit(0);
break;
case 's':
- layer2_socket_path = talloc_strdup(l23_ctx, optarg);
+ layer2_socket_path = optarg;
break;
case 'S':
sap_socket_path = talloc_strdup(l23_ctx, optarg);
break;
case 'a':
- ms->test_arfcn = atoi(optarg);
+ cfg_test_arfcn = atoi(optarg);
break;
case 'i':
gsmtap_ip = optarg;
break;
- case 'u':
- vty_ip = optarg;
- break;
- case 'v':
- vty_port = atoi(optarg);
+ case 'c':
+ config_file = optarg;
break;
case 'd':
- log_parse_category_mask(stderr_target, optarg);
+ log_cat_mask = optarg;
break;
default:
- if (app && app->cfg_handle_opt)
- app->cfg_handle_opt(c, optarg);
+ if (l23_app_info.cfg_handle_opt != NULL)
+ l23_app_info.cfg_handle_opt(c, optarg);
break;
}
}
@@ -210,19 +185,56 @@ void sighandler(int sigset)
fprintf(stderr, "Signal %d received.\n", sigset);
if (l23_app_exit)
- rc = l23_app_exit(ms);
+ rc = l23_app_exit();
+
+ if (l23_app_info.opt_supported & L23_OPT_VTY)
+ telnet_exit();
if (rc != -EBUSY)
exit (0);
}
-static void print_copyright()
+static void print_copyright(void)
{
- struct l23_app_info *app;
- app = l23_app_info();
- printf(openbsc_copyright,
- app && app->copyright ? app->copyright : "",
- app && app->contribution ? app->contribution : "");
+ printf("%s"
+ "%s\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n\n",
+ l23_app_info.copyright ? l23_app_info.copyright : "",
+ l23_app_info.contribution ? l23_app_info.contribution : "");
+}
+
+static int _vty_init(void)
+{
+ int rc;
+
+ OSMO_ASSERT(l23_app_info.vty_info != NULL);
+ l23_app_info.vty_info->tall_ctx = l23_ctx;
+
+ vty_init(l23_app_info.vty_info);
+ logging_vty_add_cmds();
+
+ if (l23_app_info.vty_init != NULL)
+ l23_app_info.vty_init();
+ if (config_file) {
+ LOGP(DLGLOBAL, LOGL_INFO, "Using configuration from '%s'\n", config_file);
+ l23_vty_reading = true;
+ rc = vty_read_config_file(config_file, NULL);
+ l23_vty_reading = false;
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_FATAL,
+ "Failed to parse the configuration file '%s'\n", config_file);
+ return rc;
+ }
+ }
+ rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB);
+ if (rc < 0) {
+ LOGP(DMOB, LOGL_FATAL, "Cannot init VTY on %s port %u: %s\n",
+ vty_get_bind_addr(), OSMO_VTY_PORT_BB, strerror(errno));
+ return rc;
+ }
+ return rc;
}
int main(int argc, char **argv)
@@ -230,52 +242,67 @@ int main(int argc, char **argv)
int rc;
INIT_LLIST_HEAD(&ms_list);
- log_init(&log_info, NULL);
- stderr_target = log_target_create_stderr();
- log_add_target(stderr_target);
- log_set_all_filter(stderr_target, 1);
l23_ctx = talloc_named_const(NULL, 1, "layer2 context");
- ms = talloc_zero(l23_ctx, struct osmocom_ms);
- if (!ms) {
- fprintf(stderr, "Failed to allocate MS\n");
- exit(1);
- }
+ osmo_init_logging2(l23_ctx, &log_info);
- print_copyright();
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_level(osmo_stderr_target, 1);
- llist_add_tail(&ms->entity, &ms_list);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_HEADER_END);
- ms->name = talloc_strdup(ms, "1");
- ms->test_arfcn = 871;
+ print_copyright();
handle_options(argc, argv);
- rc = layer2_open(ms, layer2_socket_path);
+ rc = l23_app_init();
if (rc < 0) {
- fprintf(stderr, "Failed during layer2_open()\n");
+ fprintf(stderr, "Failed during l23_app_init()\n");
exit(1);
}
- ms->lapdm_channel.lapdm_dcch.l1_ctx = ms;
- ms->lapdm_channel.lapdm_dcch.l3_ctx = ms;
- ms->lapdm_channel.lapdm_acch.l1_ctx = ms;
- ms->lapdm_channel.lapdm_acch.l3_ctx = ms;
- lapdm_channel_init(&ms->lapdm_channel, LAPDM_MODE_MS);
- lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms);
+ if (l23_app_info.opt_supported & L23_OPT_VTY) {
+ if (_vty_init() < 0)
+ exit(1);
+ }
- rc = l23_app_init(ms);
- if (rc < 0)
- exit(1);
+ if (log_cat_mask != NULL)
+ log_parse_category_mask(osmo_stderr_target, log_cat_mask);
+
+ if (l23_app_info.opt_supported & L23_OPT_TAP) {
+ if (gsmtap_ip) {
+ if (l23_cfg.gsmtap.remote_host != NULL) {
+ LOGP(DLGLOBAL, LOGL_NOTICE,
+ "Command line argument '-i %s' overrides node "
+ "'gsmtap' cmd 'remote-host %s' from the config file\n",
+ gsmtap_ip, l23_cfg.gsmtap.remote_host);
+ talloc_free(l23_cfg.gsmtap.remote_host);
+ }
+ l23_cfg.gsmtap.remote_host = talloc_strdup(l23_ctx, gsmtap_ip);
+ }
+
+ if (l23_cfg.gsmtap.remote_host) {
+ LOGP(DLGLOBAL, LOGL_NOTICE,
+ "Setting up GSMTAP Um forwarding to '%s:%u'\n",
+ l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT);
+ l23_cfg.gsmtap.inst = gsmtap_source_init(l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT, 1);
+ if (!l23_cfg.gsmtap.inst) {
+ fprintf(stderr, "Failed during gsmtap_init()\n");
+ exit(1);
+ }
+ gsmtap_source_add_sink(l23_cfg.gsmtap.inst);
+ }
+ }
- if (gsmtap_ip) {
- gsmtap_inst = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
- if (!gsmtap_inst) {
- fprintf(stderr, "Failed during gsmtap_init()\n");
+ if (l23_app_start) {
+ rc = l23_app_start();
+ if (rc < 0) {
+ fprintf(stderr, "Failed during l23_app_start()\n");
exit(1);
}
- gsmtap_source_add_sink(gsmtap_inst);
}
signal(SIGINT, sighandler);
@@ -285,7 +312,7 @@ int main(int argc, char **argv)
while (!quit) {
if (l23_app_work)
- l23_app_work(ms);
+ l23_app_work();
osmo_select_main(0);
}
diff --git a/src/host/layer23/src/common/ms.c b/src/host/layer23/src/common/ms.c
new file mode 100644
index 00000000..a14cb1eb
--- /dev/null
+++ b/src/host/layer23/src/common/ms.c
@@ -0,0 +1,80 @@
+/* Mobile Station */
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 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
+ * 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.
+ *
+ */
+#include <osmocom/gsm/gsm48.h>
+
+#include <osmocom/bb/common/ms.h>
+
+extern struct llist_head ms_list;
+
+/* Default value be configured by cmdline arg: */
+uint16_t cfg_test_arfcn = 871;
+
+static int osmocom_ms_talloc_destructor(struct osmocom_ms *ms)
+{
+
+ if (ms->sap_wq.bfd.fd > -1) {
+ sap_close(ms);
+ ms->sap_wq.bfd.fd = -1;
+ }
+
+ gprs_settings_fi(ms);
+ gsm_subscr_exit(ms);
+ gsm_sim_exit(ms);
+ return 0;
+}
+
+struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name)
+{
+ struct osmocom_ms *ms;
+
+ ms = talloc_zero(ctx, struct osmocom_ms);
+ if (!ms)
+ return NULL;
+ talloc_set_name(ms, "ms_%s", name);
+ talloc_set_destructor(ms, osmocom_ms_talloc_destructor);
+
+ ms->name = talloc_strdup(ms, name);
+ ms->test_arfcn = cfg_test_arfcn;
+ ms->lapdm_channel.lapdm_dcch.l1_ctx = ms;
+ ms->lapdm_channel.lapdm_dcch.l3_ctx = ms;
+ ms->lapdm_channel.lapdm_acch.l1_ctx = ms;
+ ms->lapdm_channel.lapdm_acch.l3_ctx = ms;
+ lapdm_channel_init(&ms->lapdm_channel, LAPDM_MODE_MS);
+ lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms);
+
+ ms->l2_wq.bfd.fd = -1;
+ ms->sap_wq.bfd.fd = -1;
+
+ ms->gmmlayer.tlli = GSM_RESERVED_TMSI;
+
+ /* Register a new MS */
+ llist_add_tail(&ms->entity, &ms_list);
+
+ gsm_support_init(ms);
+ gsm_settings_init(ms);
+ gprs_settings_init(ms);
+ /* init SAP client before SIM card starts up */
+ sap_init(ms);
+ /* SAP response call-back */
+ ms->sap_entity.sap_rsp_cb = &gsm_subscr_sap_rsp_cb;
+ gsm_sim_init(ms);
+ gsm_subscr_init(ms);
+
+ return ms;
+}
diff --git a/src/host/layer23/src/common/networks.c b/src/host/layer23/src/common/networks.c
index b4757e96..e2429ef8 100644
--- a/src/host/layer23/src/common/networks.c
+++ b/src/host/layer23/src/common/networks.c
@@ -2,11 +2,13 @@
#include <stdint.h>
#include <stdio.h>
+#include <osmocom/gsm/gsm23003.h>
+
#include <osmocom/bb/common/networks.h>
/* list of networks */
-struct gsm_networks gsm_networks[] = {
+static struct gsm_networks gsm_networks[] = {
{ 0x001, -1, "Test" },
{ 0x001, 0x01f, "Test" },
{ 0x412, -1, "Afghanistan" },
@@ -470,7 +472,7 @@ struct gsm_networks gsm_networks[] = {
{ 0x262, 0x07f, "O2" },
{ 0x262, 0x08f, "O2" },
{ 0x262, 0x09f, "Vodafone" },
- { 0x262, 0x10f, "DB Systel GSM-R" },
+ { 0x262, 0x10f, "DB Netz AG GSM-R" },
{ 0x262, 0x11f, "O2" },
{ 0x262, 0x12f, "Dolphin Telecom" },
{ 0x262, 0x13f, "Mobilcom Multimedia" },
@@ -1774,6 +1776,43 @@ struct gsm_networks gsm_networks[] = {
{ 0, 0, NULL }
};
+/* param: numerically stored mcc as per osmo_plmn_id. */
+uint16_t gsm_mcc_to_hex(uint16_t mcc)
+{
+ uint8_t buf[3];
+ uint16_t in = mcc;
+
+ buf[2] = in % 10;
+ in = in / 10;
+ buf[1] = in % 10;
+ in = in / 10;
+ buf[0] = in % 10;
+
+ return ((buf[0] << 8) +
+ (buf[1] << 4) +
+ buf[2]);
+}
+
+/* param: numerically stored mnc as per osmo_plmn_id. */
+uint16_t gsm_mnc_to_hex(uint16_t mnc, bool mnc_3_digits)
+{
+ uint8_t buf[3];
+ uint16_t in = mnc;
+ if (mnc_3_digits) {
+ buf[2] = in % 10;
+ in = in / 10;
+ } else {
+ buf[2] = 0x0f;
+ }
+ buf[1] = in % 10;
+ in = in / 10;
+ buf[0] = in % 10;
+
+ return ((buf[0] << 8) +
+ (buf[1] << 4) +
+ buf[2]);
+}
+
/* GSM 03.22 Annex A */
int gsm_match_mcc(uint16_t mcc, char *imsi)
{
@@ -1783,30 +1822,33 @@ int gsm_match_mcc(uint16_t mcc, char *imsi)
+ ((imsi[1] - '0') << 4)
+ imsi[2] - '0';
- return (mcc == sim_mcc);
+ return (gsm_mcc_to_hex(mcc) == sim_mcc);
}
/* GSM 03.22 Annex A */
-int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi)
+int gsm_match_mnc(uint16_t mcc, uint16_t mnc, bool mnc_3_digits, char *imsi)
{
uint16_t sim_mnc;
+ uint16_t mnc_hex;
/* 1. SIM-MCC = BCCH-MCC */
if (!gsm_match_mcc(mcc, imsi))
return 0;
+ mnc_hex = gsm_mnc_to_hex(mnc, mnc_3_digits);
+
/* 2. 3rd digit of BCCH-MNC is not 0xf */
- if ((mnc & 0x00f) != 0x00f) {
+ if ((mnc_hex & 0x00f) != 0x00f) {
/* 3. 3 digit SIM-MNC = BCCH-MNC */
sim_mnc = ((imsi[3] - '0') << 8)
+ ((imsi[4] - '0') << 4)
+ imsi[5] - '0';
- return (mnc == sim_mnc);
+ return (mnc_hex == sim_mnc);
}
/* 4. BCCH-MCC in the range 310-316 */
- if (mcc >= 310 && mcc <= 316) {
+ if (gsm_mcc_to_hex(mcc) >= 310 && mnc_hex <= 316) {
/* 5. 3rd diit of SIM-MNC is 0 */
if (imsi[5] != 0)
return 0;
@@ -1817,123 +1859,47 @@ int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi)
+ ((imsi[4] - '0') << 4)
+ 0x00f;
- return (mnc == sim_mnc);
-}
-
-const char *gsm_print_mcc(uint16_t mcc)
-{
- static char string[6] = "000";
-
- snprintf(string, 5, "%03x", mcc);
- return string;
-}
-
-const char *gsm_print_mnc(uint16_t mnc)
-{
- static char string[8];
-
- /* invalid format: return hex value */
- if ((mnc & 0xf000)
- || (mnc & 0x0f00) > 0x0900
- || (mnc & 0x00f0) > 0x0090
- || ((mnc & 0x000f) > 0x0009 && (mnc & 0x000f) < 0x000f)) {
- snprintf(string, 7, "0x%03x", mnc);
- return string;
- }
-
- /* two digits */
- if ((mnc & 0x000f) == 0x000f) {
- snprintf(string, 7, "%02x", mnc >> 4);
- return string;
- }
-
- /* three digits */
- snprintf(string, 7, "%03x", mnc);
- return string;
-}
-
-const uint16_t gsm_input_mcc(char *string)
-{
- uint16_t mcc;
-
- if (strlen(string) != 3)
- return GSM_INPUT_INVALID;
- if (string[0] < '0' || string [0] > '9'
- || string[1] < '0' || string [1] > '9'
- || string[2] < '0' || string [2] > '9')
- return GSM_INPUT_INVALID;
-
- mcc = ((string[0] - '0') << 8)
- | ((string[1] - '0') << 4)
- | ((string[2] - '0'));
-
- if (mcc == 0x000)
- return GSM_INPUT_INVALID;
-
- return mcc;
-}
-
-const uint16_t gsm_input_mnc(char *string)
-{
- uint16_t mnc = 0;
-
- if (strlen(string) == 2) {
- if (string[0] < '0' || string [0] > '9'
- || string[1] < '0' || string [1] > '9')
- return GSM_INPUT_INVALID;
-
- mnc = ((string[0] - '0') << 8)
- | ((string[1] - '0') << 4)
- | 0x00f;
- } else
- if (strlen(string) == 3) {
- if (string[0] < '0' || string [0] > '9'
- || string[1] < '0' || string [1] > '9'
- || string[2] < '0' || string [2] > '9')
- return GSM_INPUT_INVALID;
-
- mnc = ((string[0] - '0') << 8)
- | ((string[1] - '0') << 4)
- | ((string[2] - '0'));
- }
-
- return mnc;
+ return (mnc_hex == sim_mnc);
}
const char *gsm_get_mcc(uint16_t mcc)
{
int i;
+ uint16_t mcc_hex = gsm_mcc_to_hex(mcc);
for (i = 0; gsm_networks[i].name; i++)
- if (gsm_networks[i].mnc < 0 && gsm_networks[i].mcc == mcc)
+ if (gsm_networks[i].mnc_hex < 0 && gsm_networks[i].mcc_hex == mcc_hex)
return gsm_networks[i].name;
- return gsm_print_mcc(mcc);
+ return osmo_mcc_name(mcc);
}
-const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc)
+const char *gsm_get_mnc(const struct osmo_plmn_id *plmn)
{
int i;
+ uint16_t mcc_hex = gsm_mcc_to_hex(plmn->mcc);
+ uint16_t mnc_hex = gsm_mnc_to_hex(plmn->mnc, plmn->mnc_3_digits);
for (i = 0; gsm_networks[i].name; i++)
- if (gsm_networks[i].mcc == mcc && gsm_networks[i].mnc == mnc)
+ if (gsm_networks[i].mcc_hex == mcc_hex &&
+ gsm_networks[i].mnc_hex == mnc_hex)
return gsm_networks[i].name;
- return gsm_print_mnc(mnc);
+ return osmo_mnc_name(plmn->mnc, plmn->mnc_3_digits);
}
/* get MCC from IMSI */
const char *gsm_imsi_mcc(char *imsi)
{
int i, found = 0;
- uint16_t mcc;
+ uint16_t mcc_hex;
- mcc = ((imsi[0] - '0') << 8)
+ mcc_hex = ((imsi[0] - '0') << 8)
| ((imsi[1] - '0') << 4)
| ((imsi[2] - '0'));
for (i = 0; gsm_networks[i].name; i++) {
- if (gsm_networks[i].mcc == mcc) {
+ if (gsm_networks[i].mcc_hex == mcc_hex) {
found = 1;
break;
}
@@ -1961,15 +1927,15 @@ const char *gsm_imsi_mnc(char *imsi)
+ imsi[5] - '0';
for (i = 0; gsm_networks[i].name; i++) {
- if (gsm_networks[i].mcc != mcc)
+ if (gsm_networks[i].mcc_hex != mcc)
continue;
- if ((gsm_networks[i].mnc & 0x00f) == 0x00f) {
- if (mnc2 == gsm_networks[i].mnc) {
+ if ((gsm_networks[i].mnc_hex & 0x00f) == 0x00f) {
+ if (mnc2 == gsm_networks[i].mnc_hex) {
found++;
position = i;
}
} else {
- if (mnc3 == gsm_networks[i].mnc) {
+ if (mnc3 == gsm_networks[i].mnc_hex) {
found++;
position = i;
}
diff --git a/src/host/layer23/src/common/sap_fsm.c b/src/host/layer23/src/common/sap_fsm.c
index 22658917..f189bc8a 100644
--- a/src/host/layer23/src/common/sap_fsm.c
+++ b/src/host/layer23/src/common/sap_fsm.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -31,8 +27,10 @@
#include <osmocom/core/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
+#include <osmocom/core/write_queue.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/sap_interface.h>
diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c
index 0eac8962..0f9f8c27 100644
--- a/src/host/layer23/src/common/sap_interface.c
+++ b/src/host/layer23/src/common/sap_interface.c
@@ -18,10 +18,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 <unistd.h>
@@ -38,7 +34,7 @@
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/logging.h>
-
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/sap_interface.h>
#include <osmocom/bb/common/sap_proto.h>
#include <osmocom/bb/common/sap_fsm.h>
@@ -325,10 +321,9 @@ int _sap_close_sock(struct osmocom_ms *ms)
if (ms->sap_wq.bfd.fd <= 0)
return -EINVAL;
+ osmo_fd_unregister(&ms->sap_wq.bfd);
close(ms->sap_wq.bfd.fd);
ms->sap_wq.bfd.fd = -1;
-
- osmo_fd_unregister(&ms->sap_wq.bfd);
osmo_wqueue_clear(&ms->sap_wq);
return 0;
diff --git a/src/host/layer23/src/common/sap_proto.c b/src/host/layer23/src/common/sap_proto.c
index ecacfe05..2e891820 100644
--- a/src/host/layer23/src/common/sap_proto.c
+++ b/src/host/layer23/src/common/sap_proto.c
@@ -17,10 +17,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 <string.h>
diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/common/settings.c
index 9742b1db..6bc56340 100644
--- a/src/host/layer23/src/mobile/settings.c
+++ b/src/host/layer23/src/common/settings.c
@@ -13,25 +13,29 @@
* 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 <stdint.h>
#include <errno.h>
#include <string.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm48.h>
-#include <osmocom/bb/mobile/app_mobile.h>
+#include <osmocom/bb/common/settings.h>
#include <osmocom/bb/common/utils.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
-#include <osmocom/bb/common/networks.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+
+/* Used to set default path globally through cmdline */
+char *layer2_socket_path = L2_DEFAULT_SOCKET_PATH;
-static char *layer2_socket_path = "/tmp/osmocom_l2";
static char *sap_socket_path = "/tmp/osmocom_sap";
+static char *mncc_socket_path = "/tmp/ms_mncc";
+static char *data_socket_path = "/tmp/ms_data";
+static char *alsa_dev_default = "default";
int gsm_settings_init(struct osmocom_ms *ms)
{
@@ -41,8 +45,25 @@ int gsm_settings_init(struct osmocom_ms *ms)
strcpy(set->layer2_socket_path, layer2_socket_path);
strcpy(set->sap_socket_path, sap_socket_path);
- /* Audio settings: drop TCH frames by default */
- set->audio.io_handler = AUDIO_IOH_NONE;
+ /* Compose MNCC socket path using MS name */
+ snprintf(set->mncc_socket_path, sizeof(set->mncc_socket_path) - 1,
+ "%s_%s", mncc_socket_path, ms->name);
+
+ /* TCH voice: drop frames by default */
+ set->tch_voice.io_handler = TCH_VOICE_IOH_NONE;
+ set->tch_voice.io_format = TCH_VOICE_IOF_RTP;
+ OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_output_dev, alsa_dev_default);
+ OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_input_dev, alsa_dev_default);
+
+ /* TCH data: drop frames by default */
+ set->tch_data.io_handler = TCH_DATA_IOH_NONE;
+ set->tch_data.io_format = TCH_DATA_IOF_OSMO;
+ snprintf(set->tch_data.unix_socket_path,
+ sizeof(set->tch_data.unix_socket_path) - 1,
+ "%s_%s", data_socket_path, ms->name);
+
+ /* Built-in MNCC handler */
+ set->mncc_handler = MNCC_HANDLER_INTERNAL;
/* network search */
set->plmn_mode = PLMN_MODE_AUTO;
@@ -55,10 +76,12 @@ int gsm_settings_init(struct osmocom_ms *ms)
set->sim_type = GSM_SIM_TYPE_L1PHY;
/* test SIM */
- strcpy(set->test_imsi, "001010000000000");
- set->test_rplmn_mcc = set->test_rplmn_mnc = 1;
- set->test_lac = 0x0000;
- set->test_tmsi = 0xffffffff;
+ OSMO_STRLCPY_ARRAY(set->test_sim.imsi, "001010000000000");
+ set->test_sim.rplmn.mcc = 1;
+ set->test_sim.rplmn.mnc = 1;
+ set->test_sim.rplmn.mnc_3_digits = false;
+ set->test_sim.lac = 0x0000;
+ set->test_sim.tmsi = GSM_RESERVED_TMSI;
/* set all supported features */
set->sms_ptp = sup->sms_ptp;
@@ -87,6 +110,16 @@ int gsm_settings_init(struct osmocom_ms *ms)
set->min_rxlev_dbm = sup->min_rxlev_dbm;
set->dsc_max = sup->dsc_max;
+ set->csd_tch_f144 = sup->csd_tch_f144;
+ set->csd_tch_f96 = sup->csd_tch_f96;
+ set->csd_tch_f48 = sup->csd_tch_f48;
+ set->csd_tch_h48 = sup->csd_tch_h48;
+ set->csd_tch_f24 = sup->csd_tch_f24;
+ set->csd_tch_h24 = sup->csd_tch_h24;
+
+ set->vgcs = sup->vgcs;
+ set->vbs = sup->vbs;
+
if (sup->half_v1 || sup->half_v3)
set->half = 1;
@@ -100,6 +133,19 @@ int gsm_settings_init(struct osmocom_ms *ms)
INIT_LLIST_HEAD(&set->abbrev);
+ set->uplink_release_local = true;
+
+ set->call_params.data = (struct data_call_params) {
+ .type_rate = DATA_CALL_TR_V110_9600,
+ .transp = GSM48_BCAP_TR_TRANSP,
+
+ /* async call parameters (8-N-1) */
+ .is_async = true,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .parity = GSM48_BCAP_PAR_NONE,
+ };
+
return 0;
}
@@ -151,9 +197,6 @@ int gsm_settings_exit(struct osmocom_ms *ms)
llist_del(&abbrev->list);
talloc_free(abbrev);
}
-
- script_lua_close(ms);
-
return 0;
}
@@ -197,8 +240,72 @@ int gsm_random_imei(struct gsm_settings *set)
return 0;
}
-const struct value_string audio_io_handler_names[] = {
- { AUDIO_IOH_NONE, "none" },
- { AUDIO_IOH_LOOPBACK, "loopback" },
- { 0x00, NULL}
+const struct value_string tch_voice_io_handler_names[] = {
+ { TCH_VOICE_IOH_NONE, "none" },
+ { TCH_VOICE_IOH_GAPK, "gapk" },
+ { TCH_VOICE_IOH_L1PHY, "l1phy" },
+ { TCH_VOICE_IOH_MNCC_SOCK, "mncc-sock" },
+ { TCH_VOICE_IOH_LOOPBACK, "loopback" },
+ { 0, NULL }
};
+
+const struct value_string tch_data_io_handler_names[] = {
+ { TCH_DATA_IOH_NONE, "none" },
+ { TCH_DATA_IOH_UNIX_SOCK, "unix-sock" },
+ { TCH_DATA_IOH_LOOPBACK, "loopback" },
+ { 0, NULL }
+};
+
+const struct value_string tch_voice_io_format_names[] = {
+ { TCH_VOICE_IOF_RTP, "rtp" },
+ { TCH_VOICE_IOF_TI, "ti" },
+ { 0, NULL }
+};
+
+const struct value_string tch_data_io_format_names[] = {
+ { TCH_DATA_IOF_OSMO, "osmo" },
+ { TCH_DATA_IOF_TI, "ti" },
+ { 0, NULL }
+};
+
+int gprs_settings_init(struct osmocom_ms *ms)
+{
+ struct gprs_settings *set = &ms->gprs;
+ INIT_LLIST_HEAD(&set->apn_list);
+
+ return 0;
+}
+
+int gprs_settings_fi(struct osmocom_ms *ms)
+{
+ struct gprs_settings *set = &ms->gprs;
+ struct osmobb_apn *apn;
+ while ((apn = llist_first_entry_or_null(&set->apn_list, struct osmobb_apn, list))) {
+ /* free calls llist_del(): */
+ apn_free(apn);
+ }
+ return 0;
+}
+
+struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char *apn_name)
+{
+ struct gprs_settings *set = &ms->gprs;
+ struct osmobb_apn *apn;
+
+ llist_for_each_entry(apn, &set->apn_list, list) {
+ if (strcmp(apn->cfg.name, apn_name) == 0)
+ return apn;
+ }
+ return NULL;
+}
+
+int ms_dispatch_all_apn(struct osmocom_ms *ms, uint32_t event, void *data)
+{
+ struct gprs_settings *set = &ms->gprs;
+ int rc = 0;
+ struct osmobb_apn *apn;
+
+ llist_for_each_entry(apn, &set->apn_list, list)
+ rc |= osmo_fsm_inst_dispatch(apn->fsm.fi, event, data);
+ return rc;
+}
diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c
index 1e2bc513..4a4144ab 100644
--- a/src/host/layer23/src/common/sim.c
+++ b/src/host/layer23/src/common/sim.c
@@ -13,10 +13,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 <stdint.h>
@@ -28,11 +24,11 @@
#include <osmocom/core/gsmtap.h>
#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1ctl.h>
-extern struct gsmtap_inst *gsmtap_inst;
-
static int sim_process_job(struct osmocom_ms *ms);
/*
@@ -243,7 +239,7 @@ int gsm_sim_job_dequeue(struct osmocom_ms *ms)
sim_process_job(ms);
return 1; /* work done */
}
-
+
return 0;
}
@@ -877,7 +873,7 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
if ((ms->sim.apdu_len + length) <= sizeof(ms->sim.apdu_data)) {
memcpy(ms->sim.apdu_data + ms->sim.apdu_len, data, length);
ms->sim.apdu_len += length;
- gsmtap_send_ex(gsmtap_inst, GSMTAP_TYPE_SIM,
+ gsmtap_send_ex(l23_cfg.gsmtap.inst, GSMTAP_TYPE_SIM,
0, 0, 0, 0, 0, 0, 0, ms->sim.apdu_data, ms->sim.apdu_len);
}
}
diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/common/subscriber.c
index b2eacc59..f6aa05d5 100644
--- a/src/host/layer23/src/mobile/subscriber.c
+++ b/src/host/layer23/src/common/subscriber.c
@@ -13,10 +13,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 <stdint.h>
@@ -25,18 +21,59 @@
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/crypt/auth.h>
+#include <osmocom/gsm/gsm23003.h>
+#include <osmocom/gsm/gsm48.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/sap_interface.h>
#include <osmocom/bb/common/sap_proto.h>
#include <osmocom/bb/common/networks.h>
-#include <osmocom/bb/mobile/vty.h>
+#include <osmocom/bb/common/subscriber.h>
+#include <osmocom/bb/common/vty.h>
/* enable to get an empty list of forbidden PLMNs, even if stored on SIM.
* if list is changed, the result is not written back to SIM */
//#define TEST_EMPTY_FPLMN
+const struct value_string gsm_sub_sim_ustate_names[] = {
+ { GSM_SIM_U0_NULL, "U0_NULL" },
+ { GSM_SIM_U1_UPDATED, "U1_UPDATED" },
+ { GSM_SIM_U2_NOT_UPDATED, "U2_NOT_UPDATED" },
+ { GSM_SIM_U3_ROAMING_NA, "U3_ROAMING_NA" },
+ { 0, NULL }
+};
+
+const struct value_string gsm_sub_sim_gustate_names[] = {
+ { GSM_SIM_GU0_NULL, "GU0_NULL" },
+ { GSM_SIM_GU1_UPDATED, "GU1_UPDATED" },
+ { GSM_SIM_GU2_NOT_UPDATED, "GU2_NOT_UPDATED" },
+ { GSM_SIM_GU3_ROAMING_NA, "GU3_ROAMING_NA" },
+ { 0, NULL }
+};
+
+static int gsm_subscr_insert_simcard(struct osmocom_ms *ms);
+static int gsm_subscr_insert_testcard(struct osmocom_ms *ms);
+static int gsm_subscr_insert_sapcard(struct osmocom_ms *ms);
+
+static int gsm_subscr_remove_sapcard(struct osmocom_ms *ms);
+
+static int gsm_subscr_generate_kc_simcard(struct osmocom_ms *ms, uint8_t key_seq,
+ const uint8_t *rand, uint8_t no_sim);
+static int gsm_subscr_generate_kc_testcard(struct osmocom_ms *ms, uint8_t key_seq,
+ const uint8_t *rand, uint8_t no_sim);
+
+static int gsm_subscr_write_loci_simcard(struct osmocom_ms *ms);
+
+static int gsm_subscr_write_locigprs_simcard(struct osmocom_ms *ms);
+static int gsm_subscr_write_locigprs_testcard(struct osmocom_ms *ms);
+
+static int gsm_subscr_sim_pin_simcard(struct osmocom_ms *ms, const char *pin1, const char *pin2,
+ int8_t mode);
+
+static int subscr_write_plmn_na_simcard(struct osmocom_ms *ms);
+
static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg);
static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg);
static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg);
@@ -45,21 +82,6 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg);
* support
*/
-char *gsm_check_imsi(const char *imsi)
-{
- int i;
-
- if (!imsi || strlen(imsi) != 15)
- return "IMSI must have 15 digits!";
-
- for (i = 0; i < strlen(imsi); i++) {
- if (imsi[i] < '0' || imsi[i] > '9')
- return "IMSI must have digits 0 to 9 only!";
- }
-
- return NULL;
-}
-
static char *sim_decode_bcd(uint8_t *data, uint8_t length)
{
int i, j = 0;
@@ -81,9 +103,9 @@ static char *sim_decode_bcd(uint8_t *data, uint8_t length)
return result;
}
-/*
- * init/exit
- */
+/**************************************
+ * Generic backend-agnostic API
+ **************************************/
int gsm_subscr_init(struct osmocom_ms *ms)
{
@@ -93,8 +115,9 @@ int gsm_subscr_init(struct osmocom_ms *ms)
subscr->ms = ms;
/* set TMSI / LAC invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->gprs.ptmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* set key invalid */
subscr->key_seq = 7;
@@ -145,82 +168,554 @@ int gsm_subscr_exit(struct osmocom_ms *ms)
return 0;
}
-/*
- * test card
- */
-
-/* Attach test card, no SIM must be currently attached */
-int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
- uint16_t lac, uint32_t tmsi, uint8_t imsi_attached)
+/* Insert card */
+int gsm_subscr_insert(struct osmocom_ms *ms)
{
struct gsm_settings *set = &ms->settings;
struct gsm_subscriber *subscr = &ms->subscr;
- struct msgb *nmsg;
- char *error;
+ int rc;
if (subscr->sim_valid) {
- LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card "
- "is detached.\n");
+ LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card is removed.\n");
return -EBUSY;
}
- error = gsm_check_imsi(set->test_imsi);
- if (error) {
- LOGP(DMM, LOGL_ERROR, "%s\n", error);
- return -EINVAL;
- }
-
/* reset subscriber */
gsm_subscr_exit(ms);
gsm_subscr_init(ms);
+ subscr->sim_valid = true;
+
+ switch (set->sim_type) {
+ case GSM_SIM_TYPE_L1PHY:
+ /* trigger sim card reader process */
+ rc = gsm_subscr_insert_simcard(ms);
+ break;
+ case GSM_SIM_TYPE_TEST:
+ rc = gsm_subscr_insert_testcard(ms);
+ break;
+ case GSM_SIM_TYPE_SAP:
+ rc = gsm_subscr_insert_sapcard(ms);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc < 0) {
+ subscr->sim_valid = false;
+ return rc;
+ }
+ return rc;
+}
+
+/* Detach card */
+int gsm_subscr_remove(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n");
+ return -EINVAL;
+ }
+
+ if (subscr->sim_type == GSM_SIM_TYPE_SAP)
+ gsm_subscr_remove_sapcard(ms);
+
+ /* remove card */
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_DETACHED, ms);
+
+ return 0;
+}
+
+/* change to new U state */
+void new_sim_ustate(struct gsm_subscriber *subscr, int state)
+{
+ LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name,
+ gsm_sub_sim_ustate_name(subscr->ustate),
+ gsm_sub_sim_ustate_name(state));
+
+ subscr->ustate = state;
+}
+
+/* enter PIN */
+int gsm_subscr_sim_pin(struct osmocom_ms *ms, const char *pin1, const char *pin2,
+ int8_t mode)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ /* skip, if no real valid SIM */
+ if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid)
+ return 0;
+
+ switch (subscr->sim_type) {
+ case GSM_SIM_TYPE_L1PHY:
+ case GSM_SIM_TYPE_SAP:
+ return gsm_subscr_sim_pin_simcard(ms, pin1, pin2, mode);
+ case GSM_SIM_TYPE_TEST:
+ LOGP(DMM, LOGL_NOTICE, "PIN on test SIM: not implemented!\n");
+ return 0; /* TODO */
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, const uint8_t *rand,
+ bool no_sim)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct osmobb_l23_subscr_sim_auth_resp_sig_data sd;
+ int rc;
+
+ if (no_sim || subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid) {
+ LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n");
+ sd.ms = ms;
+ sd.sres[0] = 0x12;
+ sd.sres[1] = 0x34;
+ sd.sres[2] = 0x56;
+ sd.sres[3] = 0x78;
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_AUTH_RESP, &sd);
+ return 0;
+ }
+
+ switch (subscr->sim_type) {
+ case GSM_SIM_TYPE_TEST:
+ rc = gsm_subscr_generate_kc_testcard(ms, key_seq, rand, no_sim);
+ break;
+ case GSM_SIM_TYPE_L1PHY:
+ case GSM_SIM_TYPE_SAP:
+ /* trigger sim card reader process */
+ rc = gsm_subscr_generate_kc_simcard(ms, key_seq, rand, no_sim);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return rc;
+}
+
+/* update LOCI on SIM */
+int gsm_subscr_write_loci(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ /* skip, if no real valid SIM */
+ if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid)
+ return 0;
+
+ LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n");
+
+ switch (subscr->sim_type) {
+ case GSM_SIM_TYPE_L1PHY:
+ case GSM_SIM_TYPE_SAP:
+ return gsm_subscr_write_loci_simcard(ms);
+ case GSM_SIM_TYPE_TEST:
+ LOGP(DMM, LOGL_NOTICE, "Updating LOCI on test SIM: not implemented!\n");
+ return 0; /* TODO */
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* update LOCIGPRS on SIM */
+int gsm_subscr_write_locigprs(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ /* skip, if no real valid SIM */
+ if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid)
+ return 0;
+
+ LOGP(DMM, LOGL_INFO, "Updating LOCIGPRS on SIM\n");
+
+ switch (subscr->sim_type) {
+ case GSM_SIM_TYPE_L1PHY:
+ case GSM_SIM_TYPE_SAP:
+ return gsm_subscr_write_locigprs_simcard(ms);
+ case GSM_SIM_TYPE_TEST:
+ return gsm_subscr_write_locigprs_testcard(ms);
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* update plmn not allowed list on SIM */
+static int subscr_write_plmn_na(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ /* skip, if no real valid SIM */
+ if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid)
+ return 0;
+
+ LOGP(DMM, LOGL_INFO, "Updating FPLMN on SIM\n");
+
+ switch (subscr->sim_type) {
+ case GSM_SIM_TYPE_L1PHY:
+ case GSM_SIM_TYPE_SAP:
+ return subscr_write_plmn_na_simcard(ms);
+ case GSM_SIM_TYPE_TEST:
+ LOGP(DMM, LOGL_NOTICE, "Updating FPLMN on test SIM: not implemented!\n");
+ return 0; /* TODO */
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* del forbidden PLMN. if PLMN is NULL, flush complete list */
+int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn)
+{
+ struct gsm_sub_plmn_na *na, *na2;
+ int deleted = 0;
+
+ llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) {
+ if (!plmn || (osmo_plmn_cmp(&na->plmn, plmn) == 0)) {
+ LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden PLMNs (mcc-mnc=%s)\n",
+ osmo_plmn_name(&na->plmn));
+ llist_del(&na->entry);
+ talloc_free(na);
+ deleted = 1;
+ if (plmn)
+ break;
+ }
+ }
+
+ if (deleted) {
+ /* update plmn not allowed list on SIM */
+ subscr_write_plmn_na(subscr->ms);
+ }
+
+ return -EINVAL;
+}
+
+/* add forbidden PLMN */
+int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn, uint8_t cause)
+{
+ struct gsm_sub_plmn_na *na;
+
+ /* if already in the list, remove and add to tail */
+ gsm_subscr_del_forbidden_plmn(subscr, plmn);
+
+ LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs "
+ "(mcc-mnc=%s)\n", osmo_plmn_name(plmn));
+ na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na);
+ if (!na)
+ return -ENOMEM;
+ memcpy(&na->plmn, plmn, sizeof(struct osmo_plmn_id));
+ na->cause = cause ? : -1; /* cause 0 is not allowed */
+ llist_add_tail(&na->entry, &subscr->plmn_na);
+
+ /* don't add Home PLMN to SIM */
+ if (subscr->sim_valid && gsm_match_mnc(plmn->mcc, plmn->mnc, plmn->mnc_3_digits, subscr->imsi))
+ return -EINVAL;
+
+ /* update plmn not allowed list on SIM */
+ subscr_write_plmn_na(subscr->ms);
+
+ return 0;
+}
+
+/* search forbidden PLMN */
+int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn)
+{
+ struct gsm_sub_plmn_na *na;
+
+ llist_for_each_entry(na, &subscr->plmn_na, entry) {
+ if (osmo_plmn_cmp(&na->plmn, plmn) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr)
+{
+ if (ms->settings.force_rekey)
+ return 7;
+ else
+ return subscr->key_seq;
+}
+
+int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm_sub_plmn_na *temp;
+
+ print(priv, "MCC |MNC |cause\n");
+ print(priv, "-------+-------+-------\n");
+ llist_for_each_entry(temp, &subscr->plmn_na, entry)
+ print(priv, "%s |%-3s |#%d\n",
+ osmo_mcc_name(temp->plmn.mcc),
+ osmo_mnc_name(temp->plmn.mnc, temp->plmn.mnc_3_digits),
+ temp->cause);
+
+ return 0;
+}
+
+/* dump subscriber */
+void gsm_subscr_dump(struct gsm_subscriber *subscr,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ int i;
+ struct gsm_sub_plmn_list *plmn_list;
+ struct gsm_sub_plmn_na *plmn_na;
+
+ print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name);
+
+ if (!subscr->sim_valid) {
+ print(priv, " No SIM present.\n");
+ return;
+ }
+
+ print(priv, " IMSI: %s\n", subscr->imsi);
+ if (subscr->iccid[0])
+ print(priv, " ICCID: %s\n", subscr->iccid);
+ if (subscr->sim_spn[0])
+ print(priv, " Service Provider Name: %s\n", subscr->sim_spn);
+ if (subscr->msisdn[0])
+ print(priv, " MSISDN: %s\n", subscr->msisdn);
+ if (subscr->sms_sca[0])
+ print(priv, " SMS Service Center Address: %s\n",
+ subscr->sms_sca);
+
+ print(priv, " Status: %s IMSI %s", gsm_sub_sim_ustate_name(subscr->ustate),
+ (subscr->imsi_attached) ? "attached" : "detached");
+ if (subscr->tmsi != GSM_RESERVED_TMSI)
+ print(priv, " TMSI 0x%08x", subscr->tmsi);
+ if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) {
+ print(priv, "\n");
+ print(priv, " LAI: %s (%s, %s)\n",
+ osmo_lai_name(&subscr->lai),
+ gsm_get_mcc(subscr->lai.plmn.mcc),
+ gsm_get_mnc(&subscr->lai.plmn));
+ } else {
+ print(priv, " LAI: invalid\n");
+ }
+
+ print(priv, " GPRS Status: %s IMSI %s", gsm_sub_sim_gustate_name(subscr->gprs.gu_state),
+ (subscr->gprs.imsi_attached) ? "attached" : "detached");
+ if (subscr->gprs.ptmsi != GSM_RESERVED_TMSI)
+ print(priv, " PTMSI 0x%08x", subscr->tmsi);
+ if (subscr->gprs.ptmsi_sig != GSM_RESERVED_TMSI)
+ print(priv, " PTMSI-sig 0x%06x", subscr->gprs.ptmsi_sig);
+ if (subscr->gprs.rai.lac > 0x0000 && subscr->gprs.rai.lac < 0xfffe) {
+ struct osmo_plmn_id plmn = {
+ .mcc = subscr->gprs.rai.mcc,
+ .mnc = subscr->gprs.rai.mnc,
+ .mnc_3_digits = subscr->gprs.rai.mnc_3_digits,
+ };
+ print(priv, "\n");
+ print(priv, " RAI: %s (%s, %s)\n",
+ osmo_rai_name(&subscr->gprs.rai),
+ gsm_get_mcc(plmn.mcc),
+ gsm_get_mnc(&plmn));
+ } else {
+ print(priv, " RAI: invalid\n");
+ }
+
+ if (subscr->gprs.ptmsi != GSM_RESERVED_TMSI)
+ print(priv, " P-TMSI 0x%08x", subscr->gprs.ptmsi);
+ if (subscr->key_seq != 7) {
+ print(priv, " Key: sequence %d ", subscr->key_seq);
+ for (i = 0; i < sizeof(subscr->key); i++)
+ print(priv, " %02x", subscr->key[i]);
+ print(priv, "\n");
+ }
+ if (subscr->plmn_valid)
+ print(priv, " Registered PLMN: MCC-MNC %s (%s, %s)\n",
+ osmo_plmn_name(&subscr->plmn),
+ gsm_get_mcc(subscr->plmn.mcc),
+ gsm_get_mnc(&subscr->plmn));
+ print(priv, " Access barred cells: %s\n",
+ (subscr->acc_barr) ? "yes" : "no");
+ print(priv, " Access classes:");
+ for (i = 0; i < 16; i++)
+ if ((subscr->acc_class & (1 << i)))
+ print(priv, " C%d", i);
+ print(priv, "\n");
+ if (!llist_empty(&subscr->plmn_list)) {
+ print(priv, " List of preferred PLMNs:\n");
+ print(priv, " MCC |MNC\n");
+ print(priv, " -------+-------\n");
+ llist_for_each_entry(plmn_list, &subscr->plmn_list, entry)
+ print(priv, " %s |%s (%s, %s)\n",
+ osmo_mcc_name(plmn_list->plmn.mcc),
+ osmo_mnc_name(plmn_list->plmn.mnc, plmn_list->plmn.mnc_3_digits),
+ gsm_get_mcc(plmn_list->plmn.mcc),
+ gsm_get_mnc(&plmn_list->plmn));
+ }
+ if (!llist_empty(&subscr->plmn_na)) {
+ print(priv, " List of forbidden PLMNs:\n");
+ print(priv, " MCC |MNC |cause\n");
+ print(priv, " -------+-------+-------\n");
+ llist_for_each_entry(plmn_na, &subscr->plmn_na, entry)
+ print(priv, " %s |%-3s |#%d (%s, %s)\n",
+ osmo_mcc_name(plmn_na->plmn.mcc),
+ osmo_mnc_name(plmn_na->plmn.mnc, plmn_na->plmn.mnc_3_digits),
+ plmn_na->cause, gsm_get_mcc(plmn_na->plmn.mcc),
+ gsm_get_mnc(&plmn_na->plmn));
+ }
+}
+
+/*******************
+ * testcard backend
+ *******************/
+
+/* Attach test card, no SIM must be currently attached */
+int gsm_subscr_insert_testcard(struct osmocom_ms *ms)
+{
+ struct gsm_settings *set = &ms->settings;
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ if (!osmo_imsi_str_valid(set->test_sim.imsi)) {
+ LOGP(DMM, LOGL_ERROR, "Wrong IMSI format\n");
+ return -EINVAL;
+ }
+
subscr->sim_type = GSM_SIM_TYPE_TEST;
sprintf(subscr->sim_name, "test");
- subscr->sim_valid = 1;
- if (imsi_attached && set->test_rplmn_valid) {
- subscr->imsi_attached = imsi_attached;
- subscr->ustate = GSM_SIM_U1_UPDATED;
- } else
- subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
- subscr->acc_barr = set->test_barr; /* we may access barred cell */
+ subscr->imsi_attached = set->test_sim.imsi_attached;
+ subscr->acc_barr = set->test_sim.barr; /* we may access barred cell */
subscr->acc_class = 0xffff; /* we have any access class */
- subscr->plmn_valid = set->test_rplmn_valid;
- subscr->plmn_mcc = mcc;
- subscr->plmn_mnc = mnc;
- subscr->mcc = mcc;
- subscr->mnc = mnc;
- subscr->lac = lac;
- subscr->tmsi = tmsi;
- subscr->always_search_hplmn = set->test_always;
+ subscr->plmn_valid = set->test_sim.rplmn_valid;
+ memcpy(&subscr->plmn, &set->test_sim.rplmn, sizeof(struct osmo_plmn_id));
+ memcpy(&subscr->lai.plmn, &set->test_sim.rplmn, sizeof(struct osmo_plmn_id));
+ subscr->lai.lac = set->test_sim.lac;
+ subscr->tmsi = set->test_sim.tmsi;
+ subscr->always_search_hplmn = set->test_sim.always_search_hplmn;
subscr->t6m_hplmn = 1; /* try to find home network every 6 min */
- strcpy(subscr->imsi, set->test_imsi);
+ OSMO_STRLCPY_ARRAY(subscr->imsi, set->test_sim.imsi);
+
+ if (subscr->imsi_attached && subscr->plmn_valid)
+ subscr->ustate = GSM_SIM_U1_UPDATED;
+ else
+ subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
+
+ /* GPRS related: */
+ subscr->gprs.ptmsi = set->test_sim.locigprs.ptmsi;
+ subscr->gprs.ptmsi_sig = set->test_sim.locigprs.ptmsi_sig;
+ subscr->gprs.imsi_attached = set->test_sim.locigprs.imsi_attached;
+ subscr->gprs.rai_valid = set->test_sim.locigprs.valid;
+ memcpy(&subscr->gprs.rai, &set->test_sim.locigprs.rai, sizeof(subscr->gprs.rai));
+
+ if (subscr->gprs.imsi_attached && subscr->gprs.rai_valid)
+ subscr->ustate = GSM_SIM_U1_UPDATED;
+ else
+ subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
- LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n",
+ LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s, %s, %s)\n",
ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi),
gsm_imsi_mnc(subscr->imsi));
if (subscr->plmn_valid)
- LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s 0x%04x"
- "(%s, %s)\n", gsm_print_mcc(mcc),
- gsm_print_mnc(mnc), lac, gsm_get_mcc(mcc),
- gsm_get_mnc(mcc, mnc));
+ LOGP(DMM, LOGL_INFO, "-> Test card registered to %s"
+ " (%s, %s)\n", osmo_lai_name(&subscr->lai),
+ gsm_get_mcc(subscr->lai.plmn.mcc),
+ gsm_get_mnc(&subscr->lai.plmn));
else
LOGP(DMM, LOGL_INFO, "-> Test card not registered\n");
if (subscr->imsi_attached)
LOGP(DMM, LOGL_INFO, "-> Test card attached\n");
+
+ /* GPRS:*/
+ if (subscr->gprs.rai_valid)
+ LOGP(DMM, LOGL_INFO, "-> Test card GPRS registered to %s\n",
+ osmo_rai_name(&subscr->gprs.rai));
+ else
+ LOGP(DMM, LOGL_INFO, "-> Test card not GPRS registered\n");
+ if (subscr->gprs.imsi_attached)
+ LOGP(DMM, LOGL_INFO, "-> Test card GPRS attached\n");
+
/* insert card */
- nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ);
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_ATTACHED, ms);
+ return 0;
+}
+
+static int gsm_subscr_generate_kc_testcard(struct osmocom_ms *ms, uint8_t key_seq,
+ const uint8_t *rand, uint8_t no_sim)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct osmobb_l23_subscr_sim_auth_resp_sig_data sd;
+
+ struct gsm_settings *set = &ms->settings;
+ static struct osmo_sub_auth_data2 auth = {
+ .type = OSMO_AUTH_TYPE_GSM
+ };
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+
+ auth.algo = set->test_sim.ki_type;
+ memcpy(auth.u.gsm.ki, set->test_sim.ki, sizeof(auth.u.gsm.ki));
+ int ret = osmo_auth_gen_vec2(vec, &auth, rand);
+ if (ret < 0)
+ return ret;
+
+ /* store sequence */
+ subscr->key_seq = key_seq;
+ memcpy(subscr->key, vec->kc, 8);
+
+ LOGP(DMM, LOGL_INFO, "Sending authentication response\n");
+ sd.ms = ms;
+ memcpy(sd.sres, vec->sres, 4);
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_AUTH_RESP, &sd);
+
+ return 0;
+}
+
+/* update LOCIGPRS on test SIM */
+int gsm_subscr_write_locigprs_testcard(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+ struct sim_hdr *nsh;
+ struct gsm1111_ef_locigprs *locigprs;
+
+ /* skip, if no real valid SIM */
+ if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid)
+ return 0;
+
+ LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n");
+
+ /* write to SIM */
+ nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update,
+ SIM_JOB_UPDATE_BINARY);
if (!nmsg)
return -ENOMEM;
- gsm48_mmr_downmsg(ms, nmsg);
+ nsh = (struct sim_hdr *) nmsg->data;
+ nsh->path[0] = 0x7f20;
+ nsh->path[1] = 0;
+ nsh->file = 0x6f53;
+ locigprs = (struct gsm1111_ef_locigprs *)msgb_put(nmsg, sizeof(*locigprs));
+
+ /* P-TMSI, P-TMSI signature */
+ locigprs->ptmsi = htonl(subscr->gprs.ptmsi);
+ locigprs->ptmsi_sig_hi = htonl(subscr->gprs.ptmsi) >> 8;
+ locigprs->ptmsi_sig_lo = htonl(subscr->gprs.ptmsi) & 0xff;
+
+ /* RAI */
+ gsm48_encode_ra(&locigprs->rai, &subscr->gprs.rai);
+
+ /* location update status */
+ switch (subscr->gprs.gu_state) {
+ case GSM_SIM_GU1_UPDATED:
+ locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED;
+ break;
+ case GSM_SIM_GU3_ROAMING_NA:
+ locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED;
+ break;
+ default:
+ locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_NOT_UPDATED;
+ }
+
+ sim_job(ms, nmsg);
return 0;
}
-/*
- * sim card
- */
+/********************
+ * simcard backend
+ ********************/
static int subscr_sim_iccid(struct osmocom_ms *ms, uint8_t *data,
uint8_t length)
@@ -251,7 +746,7 @@ static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data,
/* decode IMSI, skip first digit (parity) */
imsi = sim_decode_bcd(data + 1, length);
- if (strlen(imsi) - 1 > GSM_IMSI_LENGTH - 1 || strlen(imsi) - 1 < 6) {
+ if (strlen(imsi) >= OSMO_IMSI_BUF_SIZE || strlen(imsi) - 1 < 6) {
LOGP(DMM, LOGL_NOTICE, "IMSI invalid length = %zu\n",
strlen(imsi) - 1);
return -EINVAL;
@@ -278,25 +773,60 @@ static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data,
subscr->tmsi = ntohl(loci->tmsi);
/* LAI */
- gsm48_decode_lai_hex(&loci->lai, &subscr->mcc, &subscr->mnc,
- &subscr->lac);
+ gsm48_decode_lai2(&loci->lai, &subscr->lai);
/* location update status */
switch (loci->lupd_status & 0x07) {
- case 0x00:
+ case GSM1111_EF_LOCI_LUPD_ST_UPDATED:
subscr->ustate = GSM_SIM_U1_UPDATED;
break;
- case 0x02:
- case 0x03:
+ case GSM1111_EF_LOCI_LUPD_ST_PLMN_NOT_ALLOWED:
+ case GSM1111_EF_LOCI_LUPD_ST_LA_NOT_ALLOWED:
subscr->ustate = GSM_SIM_U3_ROAMING_NA;
break;
default:
subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
}
- LOGP(DMM, LOGL_INFO, "received LOCI from SIM (mcc=%s mnc=%s lac=0x%04x "
- "U%d)\n", gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac, subscr->ustate);
+ LOGP(DMM, LOGL_INFO, "received LOCI from SIM (lai=%s U%d)\n",
+ osmo_lai_name(&subscr->lai), subscr->ustate);
+
+ return 0;
+}
+
+static int subscr_sim_locigprs(struct osmocom_ms *ms, uint8_t *data,
+ uint8_t length)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm1111_ef_locigprs *locigprs;
+
+ if (length < 11)
+ return -EINVAL;
+ locigprs = (struct gsm1111_ef_locigprs *) data;
+
+ /* P-TMSI, P-TMSI signature */
+ subscr->gprs.ptmsi = ntohl(locigprs->ptmsi);
+ subscr->gprs.ptmsi_sig = (((uint32_t)locigprs->ptmsi_sig_hi) << 8) | locigprs->ptmsi_sig_lo;
+
+ /* RAI */
+ subscr->gprs.rai_valid = true;
+ gsm48_parse_ra(&subscr->gprs.rai, (uint8_t *)&locigprs->rai);
+
+ /* routing area update status */
+ switch (locigprs->rau_status & 0x07) {
+ case GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED:
+ subscr->gprs.gu_state = GSM_SIM_GU1_UPDATED; /* TODO: use proper enums here */
+ break;
+ case GSM1111_EF_LOCIGPRS_RAU_ST_PLMN_NOT_ALLOWED:
+ case GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED:
+ subscr->gprs.gu_state = GSM_SIM_GU3_ROAMING_NA;
+ break;
+ default:
+ subscr->gprs.gu_state = GSM_SIM_GU2_NOT_UPDATED;
+ }
+
+ LOGP(DMM, LOGL_INFO, "received LOCIGPRS from SIM (RAI=%s %s)\n",
+ osmo_rai_name(&subscr->gprs.rai), gsm_sub_sim_gustate_name(subscr->gprs.gu_state));
return 0;
}
@@ -384,8 +914,6 @@ static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data,
struct gsm_subscriber *subscr = &ms->subscr;
struct gsm_sub_plmn_list *plmn;
struct llist_head *lh, *lh2;
- uint8_t lai[5];
- uint16_t dummy_lac;
/* flush list */
llist_for_each_safe(lh, lh2, &subscr->plmn_list) {
@@ -402,16 +930,11 @@ static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data,
plmn = talloc_zero(ms, struct gsm_sub_plmn_list);
if (!plmn)
return -ENOMEM;
- lai[0] = data[0];
- lai[1] = data[1];
- lai[2] = data[2];
- gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai,
- &plmn->mcc, &plmn->mnc, &dummy_lac);
+ osmo_plmn_to_bcd(&data[0], &plmn->plmn);
llist_add_tail(&plmn->entry, &subscr->plmn_list);
- LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc=%s mnc=%s) "
- "from SIM\n",
- gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc));
+ LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc-mnc=%s) from SIM\n",
+ osmo_plmn_name(&plmn->plmn));
data += 3;
length -= 3;
@@ -484,8 +1007,6 @@ static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data,
struct gsm_subscriber *subscr = &ms->subscr;
struct gsm_sub_plmn_na *na;
struct llist_head *lh, *lh2;
- uint8_t lai[5];
- uint16_t dummy_lac;
#ifdef TEST_EMPTY_FPLMN
return 0;
@@ -506,13 +1027,9 @@ static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data,
na = talloc_zero(ms, struct gsm_sub_plmn_na);
if (!na)
return -ENOMEM;
- lai[0] = data[0];
- lai[1] = data[1];
- lai[2] = data[2];
- gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, &na->mcc,
- &na->mnc, &dummy_lac);
- LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s %s from SIM\n",
- gsm_print_mcc(na->mcc), gsm_print_mnc(na->mnc));
+ osmo_plmn_to_bcd(&data[0], &na->plmn);
+ LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s from SIM\n",
+ osmo_plmn_name(&na->plmn));
na->cause = -1; /* must have a value, but SIM stores no cause */
llist_add_tail(&na->entry, &subscr->plmn_na);
@@ -533,6 +1050,7 @@ static struct subscr_sim_file {
{ 1, { 0 }, 0x2fe2, SIM_JOB_READ_BINARY, subscr_sim_iccid },
{ 1, { 0x7f20, 0 }, 0x6f07, SIM_JOB_READ_BINARY, subscr_sim_imsi },
{ 1, { 0x7f20, 0 }, 0x6f7e, SIM_JOB_READ_BINARY, subscr_sim_loci },
+ { 1, { 0x7f20, 0 }, 0x6f53, SIM_JOB_READ_BINARY, subscr_sim_locigprs },
{ 0, { 0x7f20, 0 }, 0x6f20, SIM_JOB_READ_BINARY, subscr_sim_kc },
{ 0, { 0x7f20, 0 }, 0x6f30, SIM_JOB_READ_BINARY, subscr_sim_plmnsel },
{ 0, { 0x7f20, 0 }, 0x6f31, SIM_JOB_READ_BINARY, subscr_sim_hpplmn },
@@ -560,25 +1078,18 @@ static int subscr_sim_request(struct osmocom_ms *ms)
gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi));
/* if LAI is valid, set RPLMN */
- if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) {
- subscr->plmn_valid = 1;
- subscr->plmn_mcc = subscr->mcc;
- subscr->plmn_mnc = subscr->mnc;
- LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s %s "
- "(%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc),
- gsm_print_mnc(subscr->plmn_mnc),
- gsm_get_mcc(subscr->plmn_mcc),
- gsm_get_mnc(subscr->plmn_mcc,
- subscr->plmn_mnc));
+ if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) {
+ subscr->plmn_valid = true;
+ memcpy(&subscr->plmn, &subscr->lai.plmn, sizeof(struct osmo_plmn_id));
+ LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s (%s, %s)\n",
+ osmo_plmn_name(&subscr->plmn),
+ gsm_get_mcc(subscr->plmn.mcc),
+ gsm_get_mnc(&subscr->plmn));
} else
LOGP(DMM, LOGL_INFO, "-> SIM card not registered\n");
/* insert card */
- nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ);
- if (!nmsg)
- return -ENOMEM;
- gsm48_mmr_downmsg(ms, nmsg);
-
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_ATTACHED, ms);
return 0;
}
@@ -611,7 +1122,6 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
uint16_t payload_len = msg->len - sizeof(*sh);
int rc;
struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index];
- struct msgb *nmsg;
/* error handling */
if (sh->job_type == SIM_JOB_ERROR) {
@@ -623,29 +1133,29 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DMM, LOGL_INFO, "PIN is required, %d tries left\n",
payload[1]);
- vty_notify(ms, NULL);
- vty_notify(ms, "Please give PIN for ICCID %s (you have "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Please give PIN for ICCID %s (you have "
"%d tries left)\n", subscr->iccid, payload[1]);
- subscr->sim_pin_required = 1;
+ subscr->sim_pin_required = true;
break;
case SIM_CAUSE_PIN1_BLOCKED:
LOGP(DMM, LOGL_NOTICE, "PIN is blocked\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "PIN is blocked\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "PIN is blocked\n");
if (payload[1]) {
- vty_notify(ms, "Please give PUC for ICCID %s "
+ l23_vty_ms_notify(ms, "Please give PUC for ICCID %s "
"(you have %d tries left)\n",
subscr->iccid, payload[1]);
}
- subscr->sim_pin_required = 1;
+ subscr->sim_pin_required = true;
break;
case SIM_CAUSE_PUC_BLOCKED:
LOGP(DMM, LOGL_NOTICE, "PUC is blocked\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "PUC is blocked\n");
- subscr->sim_pin_required = 1;
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "PUC is blocked\n");
+ subscr->sim_pin_required = true;
break;
default:
if (sf->func && !sf->mandatory) {
@@ -655,15 +1165,12 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
}
LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "SIM failed, replace SIM!\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "SIM failed, replace SIM!\n");
/* detach simcard */
subscr->sim_valid = 0;
- nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ);
- if (!nmsg)
- return;
- gsm48_mmr_downmsg(ms, nmsg);
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_DETACHED, ms);
}
msgb_free(msg);
@@ -672,7 +1179,7 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
/* if pin was successfully unlocked, then resend request */
if (subscr->sim_pin_required) {
- subscr->sim_pin_required = 0;
+ subscr->sim_pin_required = false;
subscr_sim_request(ms);
return;
}
@@ -686,8 +1193,8 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg)
if (rc) {
LOGP(DMM, LOGL_NOTICE, "SIM reading failed, file invalid\n");
if (subscr_sim_files[subscr->sim_file_index].mandatory) {
- vty_notify(ms, NULL);
- vty_notify(ms, "SIM failed, data invalid, replace "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "SIM failed, data invalid, replace "
"SIM!\n");
msgb_free(msg);
@@ -704,17 +1211,13 @@ ignore:
}
/* enter PIN */
-void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2,
- int8_t mode)
+static int gsm_subscr_sim_pin_simcard(struct osmocom_ms *ms, const char *pin1, const char *pin2,
+ int8_t mode)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct msgb *nmsg;
uint8_t job;
- /* skip, if no real valid SIM */
- if (!GSM_SIM_IS_READER(subscr->sim_type))
- return;
-
switch (mode) {
case -1:
job = SIM_JOB_PIN1_DISABLE;
@@ -736,7 +1239,7 @@ void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2,
default:
if (!subscr->sim_pin_required) {
LOGP(DMM, LOGL_ERROR, "No PIN required now\n");
- return;
+ return 0;
}
LOGP(DMM, LOGL_INFO, "entering PIN %s\n", pin1);
job = SIM_JOB_PIN1_UNLOCK;
@@ -744,30 +1247,20 @@ void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2,
nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job);
if (!nmsg)
- return;
+ return -ENOMEM;
memcpy(msgb_put(nmsg, strlen(pin1) + 1), pin1, strlen(pin1) + 1);
memcpy(msgb_put(nmsg, strlen(pin2) + 1), pin2, strlen(pin2) + 1);
sim_job(ms, nmsg);
+ return 0;
}
/* Attach SIM reader, no SIM must be currently attached */
-int gsm_subscr_simcard(struct osmocom_ms *ms)
+int gsm_subscr_insert_simcard(struct osmocom_ms *ms)
{
struct gsm_subscriber *subscr = &ms->subscr;
- if (subscr->sim_valid) {
- LOGP(DMM, LOGL_ERROR, "Cannot attach card, until current card "
- "is detached.\n");
- return -EBUSY;
- }
-
- /* reset subscriber */
- gsm_subscr_exit(ms);
- gsm_subscr_init(ms);
-
subscr->sim_type = GSM_SIM_TYPE_L1PHY;
sprintf(subscr->sim_name, "sim");
- subscr->sim_valid = 1;
subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
/* start with first index */
@@ -776,7 +1269,7 @@ int gsm_subscr_simcard(struct osmocom_ms *ms)
}
/* update plmn not allowed list on SIM */
-static int subscr_write_plmn_na(struct osmocom_ms *ms)
+static int subscr_write_plmn_na_simcard(struct osmocom_ms *ms)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct msgb *nmsg;
@@ -784,16 +1277,11 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms)
struct gsm_sub_plmn_na *na, *nas[4] = { NULL, NULL, NULL, NULL };
int count = 0, i;
uint8_t *data;
- uint8_t lai[5];
#ifdef TEST_EMPTY_FPLMN
return 0;
#endif
- /* skip, if no real valid SIM */
- if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid)
- return 0;
-
/* get tail list from "PLMN not allowed" */
llist_for_each_entry(na, &subscr->plmn_na, entry) {
if (count < 4)
@@ -820,11 +1308,8 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms)
nsh->file = 0x6f7b;
for (i = 0; i < 4; i++) {
if (nas[i]) {
- gsm48_encode_lai_hex((struct gsm48_loc_area_id *)lai,
- nas[i]->mcc, nas[i]->mnc, 0);
- *data++ = lai[0];
- *data++ = lai[1];
- *data++ = lai[2];
+ osmo_plmn_to_bcd(data, &nas[i]->plmn);
+ data += 3;
} else {
*data++ = 0xff;
*data++ = 0xff;
@@ -837,19 +1322,13 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms)
}
/* update LOCI on SIM */
-int gsm_subscr_write_loci(struct osmocom_ms *ms)
+static int gsm_subscr_write_loci_simcard(struct osmocom_ms *ms)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct msgb *nmsg;
struct sim_hdr *nsh;
struct gsm1111_ef_loci *loci;
- /* skip, if no real valid SIM */
- if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid)
- return 0;
-
- LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n");
-
/* write to SIM */
nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update,
SIM_JOB_UPDATE_BINARY);
@@ -865,7 +1344,7 @@ int gsm_subscr_write_loci(struct osmocom_ms *ms)
loci->tmsi = htonl(subscr->tmsi);
/* LAI */
- gsm48_encode_lai_hex(&loci->lai, subscr->mcc, subscr->mnc, subscr->lac);
+ gsm48_generate_lai2(&loci->lai, &subscr->lai);
/* TMSI time */
loci->tmsi_time = 0xff;
@@ -873,13 +1352,59 @@ int gsm_subscr_write_loci(struct osmocom_ms *ms)
/* location update status */
switch (subscr->ustate) {
case GSM_SIM_U1_UPDATED:
- loci->lupd_status = 0x00;
+ loci->lupd_status = GSM1111_EF_LOCI_LUPD_ST_UPDATED;
break;
case GSM_SIM_U3_ROAMING_NA:
- loci->lupd_status = 0x03;
+ loci->lupd_status = GSM1111_EF_LOCI_LUPD_ST_LA_NOT_ALLOWED;
+ break;
+ default:
+ loci->lupd_status = GSM1111_EF_LOCI_LUPD_ST_NOT_UPDATED;
+ }
+
+ sim_job(ms, nmsg);
+
+ return 0;
+}
+
+/* update LOCIGPRS on SIM */
+int gsm_subscr_write_locigprs_simcard(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+ struct sim_hdr *nsh;
+ struct gsm1111_ef_locigprs *locigprs;
+
+ LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n");
+
+ /* write to SIM */
+ nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update,
+ SIM_JOB_UPDATE_BINARY);
+ if (!nmsg)
+ return -ENOMEM;
+ nsh = (struct sim_hdr *) nmsg->data;
+ nsh->path[0] = 0x7f20;
+ nsh->path[1] = 0;
+ nsh->file = 0x6f53;
+ locigprs = (struct gsm1111_ef_locigprs *)msgb_put(nmsg, sizeof(*locigprs));
+
+ /* P-TMSI, P-TMSI signature */
+ locigprs->ptmsi = htonl(subscr->gprs.ptmsi);
+ locigprs->ptmsi_sig_hi = htonl(subscr->gprs.ptmsi) >> 8;
+ locigprs->ptmsi_sig_lo = htonl(subscr->gprs.ptmsi) & 0xff;
+
+ /* RAI */
+ gsm48_encode_ra(&locigprs->rai, &subscr->gprs.rai);
+
+ /* location update status */
+ switch (subscr->gprs.gu_state) {
+ case GSM_SIM_GU1_UPDATED:
+ locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED;
+ break;
+ case GSM_SIM_GU3_ROAMING_NA:
+ locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED;
break;
default:
- loci->lupd_status = 0x01;
+ locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_NOT_UPDATED;
}
sim_job(ms, nmsg);
@@ -900,63 +1425,13 @@ static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg)
msgb_free(msg);
}
-int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq,
- uint8_t *rand, uint8_t no_sim)
+static int gsm_subscr_generate_kc_simcard(struct osmocom_ms *ms, uint8_t key_seq,
+ const uint8_t *rand, uint8_t no_sim)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct msgb *nmsg;
struct sim_hdr *nsh;
- /* not a SIM */
- if (!GSM_SIM_IS_READER(subscr->sim_type)
- || !subscr->sim_valid || no_sim) {
- struct gsm48_mm_event *nmme;
-
- LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n");
- nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE);
- if (!nmsg)
- return -ENOMEM;
- nmme = (struct gsm48_mm_event *) nmsg->data;
- nmme->sres[0] = 0x12;
- nmme->sres[1] = 0x34;
- nmme->sres[2] = 0x56;
- nmme->sres[3] = 0x78;
- gsm48_mmevent_msg(ms, nmsg);
-
- return 0;
- }
-
- /* test SIM */
- if (subscr->sim_type == GSM_SIM_TYPE_TEST) {
- struct gsm48_mm_event *nmme;
- struct gsm_settings *set = &ms->settings;
- static struct osmo_sub_auth_data auth = {
- .type = OSMO_AUTH_TYPE_GSM
- };
- struct osmo_auth_vector _vec;
- struct osmo_auth_vector *vec = &_vec;
-
- auth.algo = set->test_ki_type;
- memcpy(auth.u.gsm.ki, set->test_ki, sizeof(auth.u.gsm.ki));
- int ret = osmo_auth_gen_vec(vec, &auth, rand);
- if (ret < 0)
- return ret;
-
- /* store sequence */
- subscr->key_seq = key_seq;
- memcpy(subscr->key, vec->kc, 8);
-
- LOGP(DMM, LOGL_INFO, "Sending authentication response\n");
- nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE);
- if (!nmsg)
- return -ENOMEM;
- nmme = (struct gsm48_mm_event *) nmsg->data;
- memcpy(nmme->sres, vec->sres, 4);
- gsm48_mmevent_msg(ms, nmsg);
-
- return 0;
- }
-
LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n");
/* command to SIM */
@@ -986,8 +1461,8 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg)
uint16_t payload_len = msg->len - sizeof(*sh);
struct msgb *nmsg;
struct sim_hdr *nsh;
- struct gsm48_mm_event *nmme;
uint8_t *data;
+ struct osmobb_l23_subscr_sim_auth_resp_sig_data sd;
/* error handling */
if (sh->job_type == SIM_JOB_ERROR) {
@@ -1023,278 +1498,38 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg)
sim_job(ms, nmsg);
/* return signed response */
- nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE);
- if (!nmsg)
- return;
- nmme = (struct gsm48_mm_event *) nmsg->data;
- memcpy(nmme->sres, payload, 4);
- gsm48_mmevent_msg(ms, nmsg);
-
+ sd.ms = ms;
+ memcpy(sd.sres, payload, 4);
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_AUTH_RESP, &sd);
msgb_free(msg);
}
-/*
- * detach
- */
-
-/* Detach card */
-int gsm_subscr_remove(struct osmocom_ms *ms)
-{
- struct gsm_subscriber *subscr = &ms->subscr;
- struct msgb *nmsg;
-
- if (!subscr->sim_valid) {
- LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n");
- return -EINVAL;
- }
-
- /* remove card */
- nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ);
- if (!nmsg)
- return -ENOMEM;
- gsm48_mmr_downmsg(ms, nmsg);
-
- return 0;
-}
-
-/*
- * state and lists
- */
-
-static const char *subscr_ustate_names[] = {
- "U0_NULL",
- "U1_UPDATED",
- "U2_NOT_UPDATED",
- "U3_ROAMING_NA"
-};
-
-/* change to new U state */
-void new_sim_ustate(struct gsm_subscriber *subscr, int state)
-{
- LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name,
- subscr_ustate_names[subscr->ustate],
- subscr_ustate_names[state]);
-
- subscr->ustate = state;
-}
-
-/* del forbidden PLMN. if MCC==0, flush complete list */
-int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
- uint16_t mnc)
-{
- struct gsm_sub_plmn_na *na, *na2;
- int deleted = 0;
-
- llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) {
- if (!mcc || (na->mcc == mcc && na->mnc == mnc)) {
- LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
- "PLMNs (mcc=%s, mnc=%s)\n",
- gsm_print_mcc(mcc), gsm_print_mnc(mnc));
- llist_del(&na->entry);
- talloc_free(na);
- deleted = 1;
- if (mcc)
- break;
- }
- }
-
- if (deleted) {
- /* update plmn not allowed list on SIM */
- subscr_write_plmn_na(subscr->ms);
- }
-
- return -EINVAL;
-}
-
-/* add forbidden PLMN */
-int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
- uint16_t mnc, uint8_t cause)
-{
- struct gsm_sub_plmn_na *na;
-
- /* if already in the list, remove and add to tail */
- gsm_subscr_del_forbidden_plmn(subscr, mcc, mnc);
-
- LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs "
- "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc));
- na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na);
- if (!na)
- return -ENOMEM;
- na->mcc = mcc;
- na->mnc = mnc;
- na->cause = cause ? : -1; /* cause 0 is not allowed */
- llist_add_tail(&na->entry, &subscr->plmn_na);
-
- /* don't add Home PLMN to SIM */
- if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi))
- return -EINVAL;
-
- /* update plmn not allowed list on SIM */
- subscr_write_plmn_na(subscr->ms);
-
- return 0;
-}
-
-/* search forbidden PLMN */
-int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
- uint16_t mnc)
-{
- struct gsm_sub_plmn_na *na;
-
- llist_for_each_entry(na, &subscr->plmn_na, entry) {
- if (na->mcc == mcc && na->mnc == mnc)
- return 1;
- }
-
- return 0;
-}
-
-int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr)
-{
- if (ms->settings.force_rekey)
- return 7;
- else
- return subscr->key_seq;
-}
-
-int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms,
- void (*print)(void *, const char *, ...), void *priv)
-{
- struct gsm_subscriber *subscr = &ms->subscr;
- struct gsm_sub_plmn_na *temp;
-
- print(priv, "MCC |MNC |cause\n");
- print(priv, "-------+-------+-------\n");
- llist_for_each_entry(temp, &subscr->plmn_na, entry)
- print(priv, "%s |%s%s |#%d\n",
- gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
- ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause);
-
- return 0;
-}
-
-/* dump subscriber */
-void gsm_subscr_dump(struct gsm_subscriber *subscr,
- void (*print)(void *, const char *, ...), void *priv)
-{
- int i;
- struct gsm_sub_plmn_list *plmn_list;
- struct gsm_sub_plmn_na *plmn_na;
-
- print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name);
-
- if (!subscr->sim_valid) {
- print(priv, " No SIM present.\n");
- return;
- }
-
- print(priv, " IMSI: %s\n", subscr->imsi);
- if (subscr->iccid[0])
- print(priv, " ICCID: %s\n", subscr->iccid);
- if (subscr->sim_spn[0])
- print(priv, " Service Provider Name: %s\n", subscr->sim_spn);
- if (subscr->msisdn[0])
- print(priv, " MSISDN: %s\n", subscr->msisdn);
- if (subscr->sms_sca[0])
- print(priv, " SMS Service Center Address: %s\n",
- subscr->sms_sca);
- print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate],
- (subscr->imsi_attached) ? "attached" : "detached");
- if (subscr->tmsi != 0xffffffff)
- print(priv, " TMSI 0x%08x", subscr->tmsi);
- if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) {
- print(priv, "\n");
- print(priv, " LAI: MCC %s MNC %s LAC 0x%04x "
- "(%s, %s)\n", gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac,
- gsm_get_mcc(subscr->mcc),
- gsm_get_mnc(subscr->mcc, subscr->mnc));
- } else
- print(priv, " LAI: invalid\n");
- if (subscr->key_seq != 7) {
- print(priv, " Key: sequence %d ", subscr->key_seq);
- for (i = 0; i < sizeof(subscr->key); i++)
- print(priv, " %02x", subscr->key[i]);
- print(priv, "\n");
- }
- if (subscr->plmn_valid)
- print(priv, " Registered PLMN: MCC %s MNC %s (%s, %s)\n",
- gsm_print_mcc(subscr->plmn_mcc),
- gsm_print_mnc(subscr->plmn_mnc),
- gsm_get_mcc(subscr->plmn_mcc),
- gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc));
- print(priv, " Access barred cells: %s\n",
- (subscr->acc_barr) ? "yes" : "no");
- print(priv, " Access classes:");
- for (i = 0; i < 16; i++)
- if ((subscr->acc_class & (1 << i)))
- print(priv, " C%d", i);
- print(priv, "\n");
- if (!llist_empty(&subscr->plmn_list)) {
- print(priv, " List of preferred PLMNs:\n");
- print(priv, " MCC |MNC\n");
- print(priv, " -------+-------\n");
- llist_for_each_entry(plmn_list, &subscr->plmn_list, entry)
- print(priv, " %s |%s (%s, %s)\n",
- gsm_print_mcc(plmn_list->mcc),
- gsm_print_mnc(plmn_list->mnc),
- gsm_get_mcc(plmn_list->mcc),
- gsm_get_mnc(plmn_list->mcc, plmn_list->mnc));
- }
- if (!llist_empty(&subscr->plmn_na)) {
- print(priv, " List of forbidden PLMNs:\n");
- print(priv, " MCC |MNC |cause\n");
- print(priv, " -------+-------+-------\n");
- llist_for_each_entry(plmn_na, &subscr->plmn_na, entry)
- print(priv, " %s |%s%s |#%d "
- "(%s, %s)\n", gsm_print_mcc(plmn_na->mcc),
- gsm_print_mnc(plmn_na->mnc),
- ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"",
- plmn_na->cause, gsm_get_mcc(plmn_na->mcc),
- gsm_get_mnc(plmn_na->mcc, plmn_na->mnc));
- }
-}
-
-/*
- * SAP interface integration
- */
+/***********************************************
+ * sapcard backend
+ * (SAP interface integration, reuses some parts of simcard backend)
+ ***********************************************/
/* Attach SIM card over SAP */
-int gsm_subscr_sapcard(struct osmocom_ms *ms)
+int gsm_subscr_insert_sapcard(struct osmocom_ms *ms)
{
struct gsm_subscriber *subscr = &ms->subscr;
- struct msgb *nmsg;
int rc;
- if (subscr->sim_valid) {
- LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card "
- "is detached.\n");
- return -EBUSY;
- }
-
- /* reset subscriber */
- gsm_subscr_exit(ms);
- gsm_subscr_init(ms);
-
subscr->sim_type = GSM_SIM_TYPE_SAP;
sprintf(subscr->sim_name, "sap");
- subscr->sim_valid = 1;
/* Try to connect to the SAP interface */
- vty_notify(ms, NULL);
- vty_notify(ms, "Connecting to the SAP interface...\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Connecting to the SAP interface...\n");
rc = sap_open(ms);
if (rc < 0) {
LOGP(DSAP, LOGL_ERROR, "Failed during sap_open(), no SAP based SIM reader\n");
- vty_notify(ms, "SAP connection error!\n");
+ l23_vty_ms_notify(ms, "SAP connection error!\n");
ms->sap_wq.bfd.fd = -1;
/* Detach SIM */
subscr->sim_valid = 0;
- nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ);
- if (!nmsg)
- return -ENOMEM;
- gsm48_mmr_downmsg(ms, nmsg);
+ osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_DETACHED, ms);
return rc;
}
@@ -1303,7 +1538,7 @@ int gsm_subscr_sapcard(struct osmocom_ms *ms)
}
/* Deattach sapcard */
-int gsm_subscr_remove_sapcard(struct osmocom_ms *ms)
+static int gsm_subscr_remove_sapcard(struct osmocom_ms *ms)
{
return sap_close(ms);
}
diff --git a/src/host/layer23/src/mobile/support.c b/src/host/layer23/src/common/support.c
index e9361a35..a31b456c 100644
--- a/src/host/layer23/src/mobile/support.c
+++ b/src/host/layer23/src/common/support.c
@@ -13,10 +13,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>
@@ -24,6 +20,7 @@
#include <string.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
void gsm_support_init(struct osmocom_ms *ms)
{
@@ -37,9 +34,9 @@ void gsm_support_init(struct osmocom_ms *ms)
/* revision level */
sup->rev_lev = 1; /* phase 2 mobile station */
/* support of VGCS */
- sup->vgcs = 0; /* no */
+ sup->vgcs = true; /* yes */
/* support of VBS */
- sup->vbs = 0; /* no */
+ sup->vbs = true; /* yes */
/* support of SMS */
sup->sms_ptp = 1; /* no */
/* screening indicator */
@@ -100,6 +97,14 @@ void gsm_support_init(struct osmocom_ms *ms)
sup->full_v3 = 0;
sup->half_v1 = 1;
sup->half_v3 = 0;
+
+ /* CSD modes */
+ sup->csd_tch_f144 = 0;
+ sup->csd_tch_f96 = 1;
+ sup->csd_tch_f48 = 1;
+ sup->csd_tch_h48 = 1;
+ sup->csd_tch_f24 = 1;
+ sup->csd_tch_h24 = 1;
}
/* (3.2.1) maximum channels to scan within each band */
@@ -176,6 +181,14 @@ void gsm_support_dump(struct osmocom_ms *ms,
print(priv, " Full-Rate V3 : %s\n", SUP_SET(full_v3));
print(priv, " Half-Rate V1 : %s\n", SUP_SET(half_v1));
print(priv, " Half-Rate V3 : %s\n", SUP_SET(half_v3));
+
+ print(priv, " CSD TCH/F14.4: %s\n", SUP_SET(csd_tch_f144));
+ print(priv, " CSD TCH/F9.6 : %s\n", SUP_SET(csd_tch_f96));
+ print(priv, " CSD TCH/F4.8 : %s\n", SUP_SET(csd_tch_f48));
+ print(priv, " CSD TCH/H4.8 : %s\n", SUP_SET(csd_tch_h48));
+ print(priv, " CSD TCH/F2.4 : %s\n", SUP_SET(csd_tch_f24));
+ print(priv, " CSD TCH/H2.4 : %s\n", SUP_SET(csd_tch_h24));
+
print(priv, " Min RXLEV : %d\n", set->min_rxlev_dbm);
}
diff --git a/src/host/layer23/src/common/sysinfo.c b/src/host/layer23/src/common/sysinfo.c
index f927773f..efbc5198 100644
--- a/src/host/layer23/src/common/sysinfo.c
+++ b/src/host/layer23/src/common/sysinfo.c
@@ -13,10 +13,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>
@@ -24,15 +20,18 @@
#include <string.h>
#include <arpa/inet.h>
+#include <osmocom/core/utils.h>
#include <osmocom/core/bitvec.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm48_rest_octets.h>
+
+#include <osmocom/gprs/rlcmac/csn1_defs.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/sysinfo.h>
-#define MIN(a, b) ((a < b) ? a : b)
-
/*
* dumping
*/
@@ -51,28 +50,45 @@ char *gsm_print_arfcn(uint16_t arfcn)
return text;
}
-/* check if the cell 'talks' about DCS (0) or PCS (1) */
-uint8_t gsm_refer_pcs(uint16_t arfcn, struct gsm48_sysinfo *s)
+/* Check if the cell 'talks' about DCS (false) or PCS (true) */
+bool gsm_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s)
{
/* If ARFCN is PCS band, the cell refers to PCS */
- if ((arfcn & ARFCN_PCS))
- return 1;
+ if ((cell_arfcn & ARFCN_PCS))
+ return true;
/* If no SI1 is available, we assume DCS. Be sure to call this
* function only if SI 1 is available. */
- if (!s->si1)
+ if (!cell_s->si1)
return 0;
/* If band indicator indicates PCS band, the cell refers to PCSThe */
- return s->band_ind;
+ return cell_s->band_ind;
+}
+
+/* Change the given ARFCN to PCS ARFCN, if it is in the PCS channel range and the cell refers to PCS band. */
+uint16_t gsm_arfcn_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s, uint16_t arfcn)
+{
+ /* If ARFCN is not one of the overlapping channel of PCS and DCS. */
+ if (arfcn < 512 || arfcn > 810)
+ return arfcn;
+
+ /* If the 'cell' does not refer to PCS. */
+ if (!gsm_refer_pcs(cell_arfcn, cell_s))
+ return arfcn;
+
+ /* The ARFCN is PCS, because the ARFCN is in the PCS range and the cell refers to it. */
+ return arfcn | ARFCN_PCS;
}
-int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
- void (*print)(void *, const char *, ...), void *priv, uint8_t *freq_map)
+int gsm48_sysinfo_dump(const struct gsm48_sysinfo *s, uint16_t arfcn,
+ void (*print)(void *, const char *, ...),
+ void *priv, uint8_t *freq_map)
{
- char buffer[81];
+ char buffer[82];
int i, j, k, index;
int refer_pcs = gsm_refer_pcs(arfcn, s);
+ int rc;
/* available sysinfos */
print(priv, "ARFCN = %s channels 512+ refer to %s\n",
@@ -175,7 +191,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
/* frequency map */
for (i = 0; i < 1024; i += 64) {
- sprintf(buffer, " %3d ", i);
+ snprintf(buffer, sizeof(buffer), " %3d ", i);
for (j = 0; j < 64; j++) {
index = i+j;
if (refer_pcs && index >= 512 && index <= 885)
@@ -197,7 +213,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
}
for (; j < 64; j++)
buffer[j + 5] = ' ';
- sprintf(buffer + 69, " %d", i + 63);
+ snprintf(buffer + 69, sizeof(buffer) - 69, " %d", i + 63);
print(priv, "%s\n", buffer);
}
print(priv, " 'S' = serv. cell 'n' = SI2 (neigh.) 'r' = SI5 (rep.) "
@@ -205,12 +221,10 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
/* serving cell */
print(priv, "Serving Cell:\n");
- print(priv, " BSIC = %d,%d MCC = %s MNC = %s LAC = 0x%04x Cell ID "
- "= 0x%04x\n", s->bsic >> 3, s->bsic & 0x7,
- gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
- s->cell_id);
- print(priv, " Country = %s Network Name = %s\n", gsm_get_mcc(s->mcc),
- gsm_get_mnc(s->mcc, s->mnc));
+ print(priv, " BSIC = %d,%d LAI = %s Cell ID = 0x%04x\n",
+ s->bsic >> 3, s->bsic & 0x7, osmo_lai_name(&s->lai), s->cell_id);
+ print(priv, " Country = %s Network Name = %s\n",
+ gsm_get_mcc(s->lai.plmn.mcc), gsm_get_mnc(&s->lai.plmn));
print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n",
s->max_retrans, s->tx_integer,
(s->reest_denied) ? "denied" : "allowed");
@@ -256,7 +270,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
print(priv, "\n");
/* cell selection */
- print(priv, "MX_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d "
+ print(priv, "MS_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d "
"NECI = %d ACS = %d\n", s->ms_txpwr_max_cch,
s->cell_resel_hyst_db, s->rxlev_acc_min_db, s->neci, s->acs);
@@ -285,6 +299,16 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
print(priv, " BS-PA-MFMS = %d Attachment = %s\n",
s->pag_mf_periods, (s->att_allowed) ? "allowed" : "denied");
print(priv, "BS-AG_BLKS_RES = %d ", s->bs_ag_blks_res);
+ if (!s->nch)
+ print(priv, "NCH not available ");
+ else {
+ uint8_t num_blocks, first_block;
+ rc = osmo_gsm48_si1ro_nch_pos_decode(s->nch_position, &num_blocks, &first_block);
+ if (rc < 0)
+ print(priv, "NCH Position invalid ");
+ else
+ print(priv, "NCH Position %u / %u blocks ", first_block, num_blocks);
+ }
if (s->t3212)
print(priv, "T3212 = %d sec.\n", s->t3212);
else
@@ -302,12 +326,54 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
return 0;
}
+int gsm48_si10_dump(const struct gsm48_sysinfo *s, void (*print)(void *, const char *, ...), void *priv)
+{
+ const struct si10_cell_info *c;
+ int i;
+
+ if (!s || !s->si10) {
+ print(priv, "No group channel neighbor information available.\n");
+ return 0;
+ }
+
+ if (!s->si10_cell_num) {
+ print(priv, "No group channel neighbors exist.\n");
+ return 0;
+ }
+
+ /* Group call neighbor cells. */
+ print(priv, "Group channel neighbor cells (current or last call):\n");
+ for (i = 0; i < s->si10_cell_num; i++) {
+ c = &s->si10_cell[i];
+ print(priv, " index = %d", c->index);
+ if (c->arfcn >= 0)
+ print(priv, " ARFCN = %d", c->arfcn);
+ else
+ print(priv, " ARFCN = not in SI5*");
+ print(priv, " BSIC = %d,%d", c->bsic >> 3, c->bsic & 0x7);
+ if (c->barred) {
+ print(priv, " barred");
+ continue;
+ }
+ if (c->la_different)
+ print(priv, " CRH = %d", c->cell_resel_hyst_db);
+ print(priv, " MS_TXPWR_MAX_CCCH = %d\n", c->ms_txpwr_max_cch);
+ print(priv, " RXLEV_MIN = %d", c->rxlev_acc_min_db);
+ print(priv, " CRO = %d", c->cell_resel_offset);
+ print(priv, " TEMP_OFFSET = %d", c->temp_offset);
+ print(priv, " PENALTY_TIME = %d", c->penalty_time);
+ }
+ print(priv, "\n");
+
+ return 0;
+}
+
/*
* decoding
*/
-int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc,
- uint16_t *arfcn)
+int gsm48_decode_chan_h0(const struct gsm48_chan_desc *cd,
+ uint8_t *tsc, uint16_t *arfcn)
{
*tsc = cd->h0.tsc;
*arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8);
@@ -315,8 +381,8 @@ int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc,
return 0;
}
-int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
- uint8_t *maio, uint8_t *hsn)
+int gsm48_decode_chan_h1(const struct gsm48_chan_desc *cd,
+ uint8_t *tsc, uint8_t *maio, uint8_t *hsn)
{
*tsc = cd->h1.tsc;
*maio = cd->h1.maio_low | (cd->h1.maio_high << 2);
@@ -326,8 +392,9 @@ int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
}
/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-static int decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
- uint8_t len, uint8_t mask, uint8_t frqt)
+static int decode_freq_list(struct gsm_sysinfo_freq *f,
+ const uint8_t *cd, uint8_t len,
+ uint8_t mask, uint8_t frqt)
{
#if 0
/* only Bit map 0 format for P-GSM */
@@ -341,7 +408,7 @@ static int decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
/* decode "Cell Selection Parameters" (10.5.2.4) */
static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s,
- struct gsm48_cell_sel_par *cs)
+ const struct gsm48_cell_sel_par *cs)
{
s->ms_txpwr_max_cch = cs->ms_txpwr_max_ccch;
s->cell_resel_hyst_db = cs->cell_resel_hyst * 2;
@@ -354,7 +421,7 @@ static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s,
/* decode "Cell Options (BCCH)" (10.5.2.3) */
static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s,
- struct gsm48_cell_options *co)
+ const struct gsm48_cell_options *co)
{
s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
s->bcch_dtx = co->dtx;
@@ -365,7 +432,7 @@ static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s,
/* decode "Cell Options (SACCH)" (10.5.2.3a) */
static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s,
- struct gsm48_cell_options *co)
+ const struct gsm48_cell_options *co)
{
s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
s->sacch_dtx = co->dtx;
@@ -376,7 +443,7 @@ static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s,
/* decode "Control Channel Description" (10.5.2.11) */
static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
- struct gsm48_control_channel_descr *cc)
+ const struct gsm48_control_channel_descr *cc)
{
s->ccch_conf = cc->ccch_conf;
s->bs_ag_blks_res = cc->bs_ag_blks_res;
@@ -389,7 +456,8 @@ static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
/* decode "Mobile Allocation" (10.5.2.21) */
int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq,
- uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4)
+ const uint8_t *ma, uint8_t len,
+ uint16_t *hopping, uint8_t *hopp_len, int si4)
{
int i, j = 0;
uint16_t f[len << 3];
@@ -442,16 +510,16 @@ int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq,
}
/* Rach Control decode tables */
-static uint8_t gsm48_max_retrans[4] = {
+static const uint8_t gsm48_max_retrans[4] = {
1, 2, 4, 7
};
-static uint8_t gsm48_tx_integer[16] = {
+static const uint8_t gsm48_tx_integer[16] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50
};
/* decode "RACH Control Parameter" (10.5.2.29) */
static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s,
- struct gsm48_rach_control *rc)
+ const struct gsm48_rach_control *rc)
{
s->reest_denied = rc->re;
s->cell_barr = rc->cell_bar;
@@ -462,7 +530,7 @@ static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s,
return 0;
}
static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s,
- struct gsm48_rach_control *rc)
+ const struct gsm48_rach_control *rc)
{
s->nb_reest_denied = rc->re;
s->nb_cell_barr = rc->cell_bar;
@@ -474,14 +542,13 @@ static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s,
}
/* decode "SI 1 Rest Octets" (10.5.2.32) */
-static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si,
- uint8_t len)
+static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s,
+ const uint8_t *si, uint8_t len)
{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data_len = len;
- bv.data = si;
+ struct bitvec bv = {
+ .data_len = len,
+ .data = (uint8_t *)si,
+ };
/* Optional Selection Parameters */
if (bitvec_get_bit_high(&bv) == H) {
@@ -489,23 +556,19 @@ static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si,
s->nch_position = bitvec_get_uint(&bv, 5);
} else
s->nch = 0;
- if (bitvec_get_bit_high(&bv) == H)
- s->band_ind = 1;
- else
- s->band_ind = 0;
+ s->band_ind = (bitvec_get_bit_high(&bv) == H);
return 0;
}
/* decode "SI 3 Rest Octets" (10.5.2.34) */
-static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si,
- uint8_t len)
+static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s,
+ const uint8_t *si, uint8_t len)
{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data_len = len;
- bv.data = si;
+ struct bitvec bv = {
+ .data_len = len,
+ .data = (uint8_t *)si,
+ };
/* Optional Selection Parameters */
if (bitvec_get_bit_high(&bv) == H) {
@@ -540,24 +603,23 @@ static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si,
s->sched = 0;
/* GPRS Indicator */
if (bitvec_get_bit_high(&bv) == H) {
- s->gprs = 1;
- s->gprs_ra_colour = bitvec_get_uint(&bv, 3);
- s->gprs_si13_pos = bitvec_get_uint(&bv, 1);
+ s->gprs.supported = 1;
+ s->gprs.ra_colour = bitvec_get_uint(&bv, 3);
+ s->gprs.si13_pos = bitvec_get_uint(&bv, 1);
} else
- s->gprs = 0;
+ s->gprs.supported = 0;
return 0;
}
/* decode "SI 4 Rest Octets" (10.5.2.35) */
-static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si,
- uint8_t len)
+static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s,
+ const uint8_t *si, uint8_t len)
{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data_len = len;
- bv.data = si;
+ struct bitvec bv = {
+ .data_len = len,
+ .data = (uint8_t *)si,
+ };
/* Optional Selection Parameters */
if (bitvec_get_bit_high(&bv) == H) {
@@ -576,33 +638,215 @@ static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si,
s->po = 0;
/* GPRS Indicator */
if (bitvec_get_bit_high(&bv) == H) {
- s->gprs = 1;
- s->gprs_ra_colour = bitvec_get_uint(&bv, 3);
- s->gprs_si13_pos = bitvec_get_uint(&bv, 1);
+ s->gprs.supported = 1;
+ s->gprs.ra_colour = bitvec_get_uint(&bv, 3);
+ s->gprs.si13_pos = bitvec_get_uint(&bv, 1);
} else
- s->gprs = 0;
+ s->gprs.supported = 0;
// todo: more rest octet bits
return 0;
}
-/* decode "SI 6 Rest Octets" (10.5.2.35a) */
-static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si,
- uint8_t len)
+/* TODO: decode "SI 6 Rest Octets" (10.5.2.35a) */
+static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s,
+ const uint8_t *si, uint8_t len)
{
return 0;
}
+/* Decode "SI 10 Rest Octets" (10.5.2.44) */
+static int gsm48_decode_si10_rest_first(struct gsm48_sysinfo *s, struct bitvec *bv,
+ struct si10_cell_info *c)
+{
+ uint8_t ba_ind;
+
+ /* <BA ind : bit(1)> */
+ ba_ind = bitvec_get_uint(bv, 1);
+ if (ba_ind != s->nb_ba_ind_si5) {
+ LOGP(DRR, LOGL_NOTICE, "SI10: BA_IND %u != BA_IND %u of SI5!\n", ba_ind, s->nb_ba_ind_si5);
+ return EOF;
+ }
+
+ /* { L <spare padding> | H <neighbour information> } */
+ if (bitvec_get_bit_high(bv) != H) {
+ LOGP(DRR, LOGL_INFO, "SI10: No neighbor cell defined.\n");
+ return EOF;
+ }
+
+ /* <first frequency: bit(5)> */
+ c->index = bitvec_get_uint(bv, 5);
+
+ /* <bsic : bit(6)> */
+ c->bsic = bitvec_get_uint(bv, 6);
+
+ /* { H <cell parameters> | L } */
+ if (bitvec_get_bit_high(bv) != H) {
+ LOGP(DRR, LOGL_NOTICE, "SI10: No cell parameters for first cell, cannot continue to decode!\n");
+ return EOF;
+ }
+
+ /* <cell barred (H)> | L <further cell info> */
+ if (bitvec_get_bit_high(bv) == H) {
+ c->barred = true;
+ return 0;
+ }
+
+ /* { H <cell reselect hysteresis : bit(3)> | L } */
+ if (bitvec_get_bit_high(bv) == H) {
+ c->la_different = true;
+ c->cell_resel_hyst_db = bitvec_get_uint(bv, 3) * 2;
+ }
+
+ /* <ms txpwr max cch : bit(5)> */
+ c->ms_txpwr_max_cch = bitvec_get_uint(bv, 5);
+ /* <rxlev access min : bit(6)> */
+ c->rxlev_acc_min_db = rxlev2dbm(bitvec_get_uint(bv, 6));
+ /* <cell reselect offset : bit(6)> */
+ c->cell_resel_offset = bitvec_get_uint(bv, 6);
+ /* <temporary offset : bit(3)> */
+ c->temp_offset = bitvec_get_uint(bv, 3);
+ /* <penalty time : bit(5)> */
+ c->penalty_time = bitvec_get_uint(bv, 5);
+
+ return 0;
+}
+
+static int gsm48_decode_si10_rest_other(struct gsm48_sysinfo *s, struct bitvec *bv,
+ struct si10_cell_info *c)
+{
+ int rc;
+
+ /* { H <info field> }** L <spare padding> */
+ if (bitvec_get_bit_high(bv) != H)
+ return EOF;
+
+ c->index = (c->index + 1) & 0x1f;
+ /* <next frequency (H)>** L <differential cell info> */
+ /* Increment frequency number for every <info field> and every <next frequency> occurrence. */
+ while ((rc = bitvec_get_bit_high(bv)) == H)
+ c->index = (c->index + 1) & 0x1f;
+ if (rc < 0)
+ goto short_read;
+
+ /* { H <BCC : bit(3)> | L <bsic : bit(6)> } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ rc = bitvec_get_uint(bv, 3);
+ if (rc < 0)
+ goto short_read;
+ c->bsic = (c->bsic & 0x07) | rc;
+ } else {
+ rc = bitvec_get_uint(bv, 6);
+ if (rc < 0)
+ goto short_read;
+ c->bsic = rc;
+ }
+
+ /* { H <diff cell pars> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc != H)
+ return 0;
+
+ /* <cell barred (H)> | L <further cell info> */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ c->barred = true;
+ return 0;
+ }
+
+ /* { H <cell reselect hysteresis : bit(3)> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ c->la_different = true;
+ rc = bitvec_get_uint(bv, 3);
+ if (rc < 0)
+ goto short_read;
+ c->cell_resel_hyst_db = bitvec_get_uint(bv, 3) * 2;
+ }
+
+ /* { H <ms txpwr max cch : bit(5)> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ rc = bitvec_get_uint(bv, 5);
+ if (rc < 0)
+ goto short_read;
+ c->ms_txpwr_max_cch = rc;
+ }
+
+ /* { H <rxlev access min : bit(6)> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ rc = bitvec_get_uint(bv, 6);
+ if (rc < 0)
+ goto short_read;
+ c->rxlev_acc_min_db = rxlev2dbm(rc);
+ } else
+ c->rxlev_acc_min_db = -110;
+
+ /* { H <cell reselect offset : bit(6)> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ rc = bitvec_get_uint(bv, 6);
+ if (rc < 0)
+ goto short_read;
+ c->cell_resel_offset = rc;
+ }
+
+ /* { H <temporary offset : bit(3)> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ rc = bitvec_get_uint(bv, 3);
+ if (rc < 0)
+ goto short_read;
+ c->temp_offset = rc;
+ }
+
+ /* { H <penalty time : bit(5)> | L } */
+ rc = bitvec_get_bit_high(bv);
+ if (rc < 0)
+ goto short_read;
+ if (rc == H) {
+ rc = bitvec_get_uint(bv, 5);
+ if (rc < 0)
+ goto short_read;
+ c->penalty_time = rc;
+ }
+
+ return 0;
+
+short_read:
+ LOGP(DRR, LOGL_NOTICE, "SI10: Short read of differential cell info.\n");
+ return -EINVAL;
+}
+
int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_1 *si, int len)
+ const struct gsm48_system_information_type_1 *si, int len)
{
int payload_len = len - sizeof(*si);
- memcpy(s->si1_msg, si, MIN(len, sizeof(s->si1_msg)));
+ memcpy(s->si1_msg, si, OSMO_MIN(len, sizeof(s->si1_msg)));
/* Cell Channel Description */
decode_freq_list(s->freq, si->cell_channel_description,
- sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
+ sizeof(si->cell_channel_description),
+ 0xce, FREQ_TYPE_SERV);
/* RACH Control Parameter */
gsm48_decode_rach_ctl_param(s, &si->rach_control);
/* SI 1 Rest Octets */
@@ -612,26 +856,26 @@ int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s,
s->si1 = 1;
if (s->si4) {
- LOGP(DRR, LOGL_NOTICE, "Now updating previously received "
- "SYSTEM INFORMATION 4\n");
- gsm48_decode_sysinfo4(s,
- (struct gsm48_system_information_type_4 *) s->si4_msg,
- sizeof(s->si4_msg));
+ const struct gsm48_system_information_type_4 *si4 = (void *)s->si4_msg;
+ LOGP(DRR, LOGL_NOTICE,
+ "Now updating previously received SYSTEM INFORMATION 4\n");
+ gsm48_decode_sysinfo4(s, si4, sizeof(s->si4_msg));
}
return 0;
}
int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_2 *si, int len)
+ const struct gsm48_system_information_type_2 *si, int len)
{
- memcpy(s->si2_msg, si, MIN(len, sizeof(s->si2_msg)));
+ memcpy(s->si2_msg, si, OSMO_MIN(len, sizeof(s->si2_msg)));
/* Neighbor Cell Description */
- s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1;
- s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 4) & 1;
decode_freq_list(s->freq, si->bcch_frequency_list,
- sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
+ sizeof(si->bcch_frequency_list),
+ 0xce, FREQ_TYPE_NCELL_2);
/* NCC Permitted */
s->nb_ncc_permitted_si2 = si->ncc_permitted;
/* RACH Control Parameter */
@@ -643,13 +887,13 @@ int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s,
}
int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_2bis *si, int len)
+ const struct gsm48_system_information_type_2bis *si, int len)
{
- memcpy(s->si2b_msg, si, MIN(len, sizeof(s->si2b_msg)));
+ memcpy(s->si2b_msg, si, OSMO_MIN(len, sizeof(s->si2b_msg)));
/* Neighbor Cell Description */
- s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1;
- s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 4) & 1;
decode_freq_list(s->freq, si->bcch_frequency_list,
sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis);
/* RACH Control Parameter */
@@ -661,13 +905,13 @@ int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s,
}
int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_2ter *si, int len)
+ const struct gsm48_system_information_type_2ter *si, int len)
{
- memcpy(s->si2t_msg, si, MIN(len, sizeof(s->si2t_msg)));
+ memcpy(s->si2t_msg, si, OSMO_MIN(len, sizeof(s->si2t_msg)));
/* Neighbor Cell Description 2 */
- s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3;
- s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 1;
+ s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 3;
+ s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 4) & 1;
decode_freq_list(s->freq, si->ext_bcch_frequency_list,
sizeof(si->ext_bcch_frequency_list), 0x8e,
FREQ_TYPE_NCELL_2ter);
@@ -678,16 +922,16 @@ int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s,
}
int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_3 *si, int len)
+ const struct gsm48_system_information_type_3 *si, int len)
{
int payload_len = len - sizeof(*si);
- memcpy(s->si3_msg, si, MIN(len, sizeof(s->si3_msg)));
+ memcpy(s->si3_msg, si, OSMO_MIN(len, sizeof(s->si3_msg)));
/* Cell Identity */
s->cell_id = ntohs(si->cell_identity);
/* LAI */
- gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ gsm48_decode_lai2(&si->lai, &s->lai);
/* Control Channel Description */
gsm48_decode_ccd(s, &si->control_channel_desc);
/* Cell Options (BCCH) */
@@ -700,9 +944,8 @@ int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s,
if (payload_len >= 4)
gsm48_decode_si3_rest(s, si->rest_octets, payload_len);
- LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s "
- "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc), s->lac);
+ LOGP(DRR, LOGL_INFO,
+ "New SYSTEM INFORMATION 3 (lai=%s)\n", osmo_lai_name(&s->lai));
s->si3 = 1;
@@ -710,17 +953,17 @@ int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s,
}
int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_4 *si, int len)
+ const struct gsm48_system_information_type_4 *si, int len)
{
int payload_len = len - sizeof(*si);
- uint8_t *data = si->data;
- struct gsm48_chan_desc *cd;
+ const uint8_t *data = si->data;
+ const struct gsm48_chan_desc *cd;
- memcpy(s->si4_msg, si, MIN(len, sizeof(s->si4_msg)));
+ memcpy(s->si4_msg, si, OSMO_MIN(len, sizeof(s->si4_msg)));
/* LAI */
- gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ gsm48_decode_lai2(&si->lai, &s->lai);
/* Cell Selection Parameters */
gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
/* RACH Control Parameter */
@@ -733,15 +976,13 @@ short_read:
LOGP(DRR, LOGL_NOTICE, "Short read!\n");
return -EIO;
}
- cd = (struct gsm48_chan_desc *) (data + 1);
+ cd = (const struct gsm48_chan_desc *)(data + 1);
s->chan_nr = cd->chan_nr;
- if (cd->h0.h) {
- s->h = 1;
+ s->h = cd->h0.h;
+ if (s->h)
gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn);
- } else {
- s->h = 0;
+ else
gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn);
- }
payload_len -= 4;
data += 4;
}
@@ -751,11 +992,10 @@ short_read:
goto short_read;
if (!s->si1) {
LOGP(DRR, LOGL_NOTICE, "Ignoring CBCH allocation of "
- "SYSTEM INFORMATION 4 until SI 1 is "
- "received.\n");
+ "SYSTEM INFORMATION 4 until SI 1 is received.\n");
} else {
gsm48_decode_mobile_alloc(s->freq, data + 2, data[1],
- s->hopping, &s->hopp_len, 1);
+ s->hopping, &s->hopp_len, 1);
}
payload_len -= 2 + data[1];
data += 2 + data[1];
@@ -770,59 +1010,65 @@ short_read:
}
int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_5 *si, int len)
+ const struct gsm48_system_information_type_5 *si, int len)
{
- memcpy(s->si5_msg, si, MIN(len, sizeof(s->si5_msg)));
+ memcpy(s->si5_msg, si, OSMO_MIN(len, sizeof(s->si5_msg)));
/* Neighbor Cell Description */
- s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1;
- s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 4) & 1;
decode_freq_list(s->freq, si->bcch_frequency_list,
- sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
+ sizeof(si->bcch_frequency_list),
+ 0xce, FREQ_TYPE_REP_5);
s->si5 = 1;
+ s->si10 = false;
return 0;
}
int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_5bis *si, int len)
+ const struct gsm48_system_information_type_5bis *si, int len)
{
- memcpy(s->si5b_msg, si, MIN(len, sizeof(s->si5b_msg)));
+ memcpy(s->si5b_msg, si, OSMO_MIN(len, sizeof(s->si5b_msg)));
/* Neighbor Cell Description */
- s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1;
- s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 4) & 1;
decode_freq_list(s->freq, si->bcch_frequency_list,
- sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
+ sizeof(si->bcch_frequency_list),
+ 0xce, FREQ_TYPE_REP_5bis);
s->si5bis = 1;
+ s->si10 = false;
return 0;
}
int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_5ter *si, int len)
+ const struct gsm48_system_information_type_5ter *si, int len)
{
- memcpy(s->si5t_msg, si, MIN(len, sizeof(s->si5t_msg)));
+ memcpy(s->si5t_msg, si, OSMO_MIN(len, sizeof(s->si5t_msg)));
/* Neighbor Cell Description */
- s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 6) & 3;
- s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 5) & 1;
+ s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 5) & 3;
+ s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 4) & 1;
decode_freq_list(s->freq, si->bcch_frequency_list,
- sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter);
+ sizeof(si->bcch_frequency_list),
+ 0x8e, FREQ_TYPE_REP_5ter);
s->si5ter = 1;
+ s->si10 = false;
return 0;
}
int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s,
- struct gsm48_system_information_type_6 *si, int len)
+ const struct gsm48_system_information_type_6 *si, int len)
{
int payload_len = len - sizeof(*si);
- memcpy(s->si6_msg, si, MIN(len, sizeof(s->si6_msg)));
+ memcpy(s->si6_msg, si, OSMO_MIN(len, sizeof(s->si6_msg)));
/* Cell Identity */
if (s->si6 && s->cell_id != ntohs(si->cell_identity))
@@ -830,7 +1076,7 @@ int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s,
"read.\n");
s->cell_id = ntohs(si->cell_identity);
/* LAI */
- gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ gsm48_decode_lai2(&si->lai, &s->lai);
/* Cell Options (SACCH) */
gsm48_decode_cellopt_sacch(s, &si->cell_options);
/* NCC Permitted */
@@ -844,28 +1090,156 @@ int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s,
return 0;
}
-int gsm48_encode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t mcc,
- uint16_t mnc, uint16_t lac)
+/* Get ARFCN from BCCH allocation found in SI5/SI5bis an SI5ter. See TS 44.018 §10.5.2.20. */
+int16_t arfcn_from_freq_index(const struct gsm48_sysinfo *s, uint16_t index)
{
- lai->digits[0] = (mcc >> 8) | (mcc & 0xf0);
- lai->digits[1] = (mcc & 0x0f) | (mnc << 4);
- lai->digits[2] = (mnc >> 8) | (mnc & 0xf0);
- lai->lac = htons(lac);
+ uint16_t arfcn, i = 0;
+
+ /* Search for ARFCN found in SI5 or SI5bis. (first sub list) */
+ for (arfcn = 1; arfcn <= 1024; arfcn++) {
+ if (!(s->freq[arfcn & 1023].mask & (FREQ_TYPE_REP_5 | FREQ_TYPE_REP_5bis)))
+ continue;
+ if (index == i++)
+ return arfcn & 1023;
+ }
- return 0;
+ /* Search for ARFCN found in SI5ter. (second sub list) */
+ for (arfcn = 1; arfcn <= 1024; arfcn++) {
+ if (!(s->freq[arfcn & 1023].mask & FREQ_TYPE_REP_5ter))
+ continue;
+ if (index == i++)
+ return arfcn & 1023;
+ }
+
+ /* If not found, return EOF (-1) as idicator. */
+ return EOF;
}
- int gsm48_decode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t *mcc,
- uint16_t *mnc, uint16_t *lac)
+int gsm48_decode_sysinfo10(struct gsm48_sysinfo *s,
+ const struct gsm48_system_information_type_10 *si, int len)
{
- *mcc = ((lai->digits[0] & 0x0f) << 8)
- | (lai->digits[0] & 0xf0)
- | (lai->digits[1] & 0x0f);
- *mnc = ((lai->digits[2] & 0x0f) << 8)
- | (lai->digits[2] & 0xf0)
- | ((lai->digits[1] & 0xf0) >> 4);
- *lac = ntohs(lai->lac);
-
+ int payload_len = len - sizeof(*si);
+ struct bitvec bv;
+ int i;
+ int rc;
+
+ bv = (struct bitvec) {
+ .data_len = payload_len,
+ .data = (uint8_t *)si->rest_octets,
+ };
+
+ memcpy(s->si10_msg, si, OSMO_MIN(len, sizeof(s->si10_msg)));
+
+ /* Clear cell list. */
+ s->si10_cell_num = 0;
+ memset(s->si10_cell, 0, sizeof(s->si10_cell));
+
+ /* SI 10 Rest Octets of first neighbor cell, if included. */
+ rc = gsm48_decode_si10_rest_first(s, &bv, &s->si10_cell[0]);
+ if (rc == EOF) {
+ s->si10 = true;
+ return 0;
+ }
+ if (rc < 0)
+ return rc;
+ s->si10_cell[0].arfcn = arfcn_from_freq_index(s, s->si10_cell[0].index);
+ s->si10_cell_num++;
+
+ for (i = 1; i < ARRAY_SIZE(s->si10_cell); i++) {
+ /* Clone last cell info and then store differential elements. */
+ memcpy(&s->si10_cell[i], &s->si10_cell[i - 1], sizeof(s->si10_cell[i]));
+ /* SI 10 Rest Octets of other neighbor cell, if included. */
+ rc = gsm48_decode_si10_rest_other(s, &bv, &s->si10_cell[i]);
+ if (rc == EOF)
+ break;
+ if (rc < 0)
+ return rc;
+ s->si10_cell[i].arfcn = arfcn_from_freq_index(s, s->si10_cell[i].index);
+ s->si10_cell_num++;
+ }
+
+ s->si10 = true;
return 0;
}
+int gsm48_decode_sysinfo13(struct gsm48_sysinfo *s,
+ const struct gsm48_system_information_type_13 *si, int len)
+{
+ SI13_RestOctets_t si13ro;
+ int rc;
+
+ memcpy(s->si13_msg, si, OSMO_MIN(len, sizeof(s->si13_msg)));
+
+ rc = osmo_gprs_rlcmac_decode_si13ro(&si13ro, si->rest_octets, sizeof(s->si13_msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to parse SI13 Rest Octets\n");
+ return rc;
+ }
+
+ if (si13ro.UnionType != 0) {
+ LOGP(DRR, LOGL_NOTICE, "PBCCH is deprecated and not supported\n");
+ return -ENOTSUP;
+ }
+
+ s->gprs.hopping = si13ro.Exist_MA;
+ if (s->gprs.hopping) {
+ const GPRS_Mobile_Allocation_t *gma = &si13ro.GPRS_Mobile_Allocation;
+
+ s->gprs.hsn = gma->HSN;
+ s->gprs.rfl_num_len = gma->ElementsOf_RFL_NUMBER;
+ memcpy(&s->gprs.rfl_num[0], &gma->RFL_NUMBER[0], sizeof(gma->RFL_NUMBER));
+
+ if (gma->UnionType == 0) { /* MA Bitmap */
+ const MobileAllocation_t *ma = &gma->u.MA;
+ s->gprs.ma_bitlen = ma->MA_BitLength;
+ memcpy(&s->gprs.ma_bitmap[0], &ma->MA_BITMAP[0], sizeof(ma->MA_BITMAP));
+ } else { /* ARFCN Index List */
+ const ARFCN_index_list_t *ai = &gma->u.ARFCN_index_list;
+ s->gprs.arfcn_idx_len = ai->ElementsOf_ARFCN_INDEX;
+ memcpy(&s->gprs.arfcn_idx[0], &ai->ARFCN_INDEX[0], sizeof(ai->ARFCN_INDEX));
+ }
+ }
+
+ const PBCCH_Not_present_t *np = &si13ro.u.PBCCH_Not_present;
+
+ s->gprs.rac = np->RAC;
+ s->gprs.prio_acc_thresh = np->PRIORITY_ACCESS_THR;
+ s->gprs.nco = np->NETWORK_CONTROL_ORDER;
+
+ const GPRS_Cell_Options_t *gco = &np->GPRS_Cell_Options;
+
+ s->gprs.nmo = gco->NMO;
+ s->gprs.T3168 = gco->T3168;
+ s->gprs.T3192 = gco->T3192;
+ s->gprs.ab_type = gco->ACCESS_BURST_TYPE;
+ s->gprs.ctrl_ack_type_use_block = !gco->CONTROL_ACK_TYPE; /* inverted */
+ s->gprs.bs_cv_max = gco->BS_CV_MAX;
+
+ s->gprs.pan_params_present = gco->Exist_PAN;
+ if (s->gprs.pan_params_present) {
+ s->gprs.pan_dec = gco->PAN_DEC;
+ s->gprs.pan_inc = gco->PAN_INC;
+ s->gprs.pan_max = gco->PAN_MAX;
+ }
+
+ s->gprs.egprs_supported = 0;
+ if (gco->Exist_Extension_Bits) {
+ /* CSN.1 codec is not powerful enough (yet?) to decode this part :( */
+ unsigned int ext_len = gco->Extension_Bits.extension_length;
+ const uint8_t *ext = &gco->Extension_Bits.Extension_Info[0];
+
+ s->gprs.egprs_supported = (ext[0] >> 7);
+ if (s->gprs.egprs_supported) {
+ if (ext_len < 6)
+ return -EINVAL;
+ s->gprs.egprs_pkt_chan_req = ~ext[0] & (1 << 6); /* inverted */
+ s->gprs.egprs_bep_period = (ext[0] >> 2) & 0x0f;
+ }
+ }
+
+ /* TODO: GPRS_Power_Control_Parameters */
+
+ s->si13 = 1;
+
+ return 0;
+}
diff --git a/src/host/layer23/src/common/utils.c b/src/host/layer23/src/common/utils.c
index 4ecb134b..417519b0 100644
--- a/src/host/layer23/src/common/utils.c
+++ b/src/host/layer23/src/common/utils.c
@@ -14,10 +14,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 <osmocom/bb/common/utils.h>
diff --git a/src/host/layer23/src/common/vty.c b/src/host/layer23/src/common/vty.c
new file mode 100644
index 00000000..dd80a14b
--- /dev/null
+++ b/src/host/layer23/src/common/vty.c
@@ -0,0 +1,1499 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ * (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
+ * 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.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/crypt/auth.h>
+#include <osmocom/vty/cpu_sched_vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/misc.h>
+
+#include <osmocom/bb/common/vty.h>
+#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/networks.h>
+#include <osmocom/bb/common/gps.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+
+extern struct llist_head active_connections; /* libosmocore */
+
+bool l23_vty_reading = false;
+
+bool l23_vty_hide_default = false;
+
+static struct cmd_node ms_node = {
+ MS_NODE,
+ "%s(ms)# ",
+ 1
+};
+
+static struct cmd_node gsmtap_node = {
+ GSMTAP_NODE,
+ "%s(gsmtap)# ",
+ 1
+};
+
+struct cmd_node testsim_node = {
+ TESTSIM_NODE,
+ "%s(test-sim)# ",
+ 1
+};
+
+static void l23_vty_restart_required_warn(struct vty *vty, struct osmocom_ms *ms)
+{
+ if (l23_vty_reading)
+ return;
+ if (ms->shutdown != MS_SHUTDOWN_NONE)
+ return;
+ vty_out(vty, "You must restart MS '%s' ('shutdown / no shutdown') for "
+ "change to take effect!%s", ms->name, VTY_NEWLINE);
+}
+
+struct osmocom_ms *l23_vty_get_ms(const char *name, struct vty *vty)
+{
+ struct osmocom_ms *ms;
+
+ llist_for_each_entry(ms, &ms_list, entity) {
+ if (!strcmp(ms->name, name)) {
+ if (ms->shutdown != MS_SHUTDOWN_NONE) {
+ vty_out(vty, "MS '%s' is admin down.%s", name,
+ VTY_NEWLINE);
+ return NULL;
+ }
+ return ms;
+ }
+ }
+ vty_out(vty, "MS name '%s' does not exist.%s", name, VTY_NEWLINE);
+
+ return NULL;
+}
+
+void l23_vty_ms_notify(struct osmocom_ms *ms, const char *fmt, ...)
+{
+ struct telnet_connection *connection;
+ char buffer[1000];
+ va_list args;
+ struct vty *vty;
+
+ if (fmt) {
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ if (!buffer[0])
+ return;
+ }
+
+ llist_for_each_entry(connection, &active_connections, entry) {
+ vty = connection->vty;
+ if (!vty)
+ continue;
+ if (!fmt) {
+ vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name,
+ VTY_NEWLINE);
+ continue;
+ }
+ if (buffer[strlen(buffer) - 1] == '\n') {
+ buffer[strlen(buffer) - 1] = '\0';
+ vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE);
+ buffer[strlen(buffer)] = '\n';
+ } else
+ vty_out(vty, "%% %s", buffer);
+ }
+}
+
+void l23_vty_printf(void *priv, const char *fmt, ...)
+{
+ char buffer[1000];
+ struct vty *vty = priv;
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ if (buffer[0]) {
+ if (buffer[strlen(buffer) - 1] == '\n') {
+ buffer[strlen(buffer) - 1] = '\0';
+ vty_out(vty, "%s%s", buffer, VTY_NEWLINE);
+ } else
+ vty_out(vty, "%s", buffer);
+ }
+}
+
+/* placeholder for layer23 shared MS info to be dumped */
+void l23_ms_dump(struct osmocom_ms *ms, struct vty *vty)
+{
+ struct gsm_settings *set = &ms->settings;
+ char *service = "";
+
+ if (!ms->started)
+ service = ", radio is not started";
+ else if (ms->mmlayer.state == GSM48_MM_ST_NULL) {
+ service = ", MM connection not yet set up";
+ } else if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) {
+ /* current MM idle state */
+ switch (ms->mmlayer.substate) {
+ case GSM48_MM_SST_NORMAL_SERVICE:
+ case GSM48_MM_SST_PLMN_SEARCH_NORMAL:
+ service = ", service is normal";
+ break;
+ case GSM48_MM_SST_LOC_UPD_NEEDED:
+ case GSM48_MM_SST_ATTEMPT_UPDATE:
+ service = ", service is limited (pending)";
+ break;
+ case GSM48_MM_SST_NO_CELL_AVAIL:
+ service = ", service is unavailable";
+ break;
+ default:
+ if (ms->subscr.sim_valid)
+ service = ", service is limited";
+ else
+ service = ", service is limited "
+ "(IMSI detached)";
+ break;
+ }
+ } else
+ service = ", MM connection active";
+
+ vty_out(vty, "MS '%s' is %s%s%s%s", ms->name,
+ (ms->shutdown != MS_SHUTDOWN_NONE) ? "administratively " : "",
+ (ms->shutdown != MS_SHUTDOWN_NONE || !ms->started) ? "down" : "up",
+ (ms->shutdown == MS_SHUTDOWN_NONE) ? service : "",
+ VTY_NEWLINE);
+
+ vty_out(vty, " IMEI: %s%s", set->imei, VTY_NEWLINE);
+ vty_out(vty, " IMEISV: %s%s", set->imeisv, VTY_NEWLINE);
+ if (set->imei_random)
+ vty_out(vty, " IMEI generation: random (%d trailing "
+ "digits)%s", set->imei_random, VTY_NEWLINE);
+ else
+ vty_out(vty, " IMEI generation: fixed%s", VTY_NEWLINE);
+}
+
+/* CONFIG NODE: */
+DEFUN(cfg_hide_default, cfg_hide_default_cmd, "hide-default",
+ "Hide most default values in config to make it more compact")
+{
+ l23_vty_hide_default = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_hide_default, cfg_no_hide_default_cmd, "no hide-default",
+ NO_STR "Show default values in config")
+{
+ l23_vty_hide_default = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_support, show_support_cmd, "show support [MS_NAME]",
+ SHOW_STR "Display information about MS support\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ if (argc) {
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ gsm_support_dump(ms, l23_vty_printf, vty);
+ } else {
+ llist_for_each_entry(ms, &ms_list, entity) {
+ gsm_support_dump(ms, l23_vty_printf, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_subscr, show_subscr_cmd, "show subscriber [MS_NAME]",
+ SHOW_STR "Display information about subscriber\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ if (argc) {
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ gsm_subscr_dump(&ms->subscr, l23_vty_printf, vty);
+ } else {
+ llist_for_each_entry(ms, &ms_list, entity) {
+ if (ms->shutdown == MS_SHUTDOWN_NONE) {
+ gsm_subscr_dump(&ms->subscr, l23_vty_printf, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "gsmtap" config */
+gDEFUN(l23_cfg_gsmtap, l23_cfg_gsmtap_cmd, "gsmtap",
+ "Configure GSMTAP\n")
+{
+ vty->node = GSMTAP_NODE;
+ return CMD_SUCCESS;
+}
+
+static const struct value_string gsmtap_categ_gprs_names[] = {
+ { L23_GSMTAP_GPRS_C_DL_UNKNOWN, "dl-unknown" },
+ { L23_GSMTAP_GPRS_C_DL_DUMMY, "dl-dummy" },
+ { L23_GSMTAP_GPRS_C_DL_CTRL, "dl-ctrl" },
+ { L23_GSMTAP_GPRS_C_DL_DATA_GPRS, "dl-data-gprs" },
+ { L23_GSMTAP_GPRS_C_DL_DATA_EGPRS, "dl-data-egprs" },
+ { L23_GSMTAP_GPRS_C_UL_UNKNOWN, "ul-unknown" },
+ { L23_GSMTAP_GPRS_C_UL_DUMMY, "ul-dummy" },
+ { L23_GSMTAP_GPRS_C_UL_CTRL, "ul-ctrl" },
+ { L23_GSMTAP_GPRS_C_UL_DATA_GPRS, "ul-data-gprs" },
+ { L23_GSMTAP_GPRS_C_UL_DATA_EGPRS, "ul-data-egprs" },
+ { 0, NULL }
+};
+
+static const struct value_string gsmtap_categ_gprs_help[] = {
+ { L23_GSMTAP_GPRS_C_DL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" },
+ { L23_GSMTAP_GPRS_C_DL_DUMMY, "Downlink Dummy Blocks" },
+ { L23_GSMTAP_GPRS_C_DL_CTRL, "Downlink Control Blocks" },
+ { L23_GSMTAP_GPRS_C_DL_DATA_GPRS, "Downlink Data Blocks (GPRS)" },
+ { L23_GSMTAP_GPRS_C_DL_DATA_EGPRS, "Downlink Data Blocks (EGPRS)" },
+ { L23_GSMTAP_GPRS_C_UL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" },
+ { L23_GSMTAP_GPRS_C_UL_DUMMY, "Uplink Dummy Blocks" },
+ { L23_GSMTAP_GPRS_C_UL_CTRL, "Uplink Control Blocks" },
+ { L23_GSMTAP_GPRS_C_UL_DATA_GPRS, "Uplink Data Blocks (GPRS)" },
+ { L23_GSMTAP_GPRS_C_UL_DATA_EGPRS, "Uplink Data Blocks (EGPRS)" },
+ { 0, NULL }
+};
+
+DEFUN(cfg_gsmtap_gsmtap_remote_host,
+ cfg_gsmtap_gsmtap_remote_host_cmd,
+ "remote-host [HOSTNAME]",
+ "Enable GSMTAP Um logging\n"
+ "Remote IP address or hostname ('localhost' if omitted)\n")
+{
+ osmo_talloc_replace_string(l23_ctx, &l23_cfg.gsmtap.remote_host,
+ argc > 0 ? argv[0] : "localhost");
+
+ if (vty->type != VTY_FILE)
+ vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_no_gsmtap_remote_host,
+ cfg_gsmtap_no_gsmtap_remote_host_cmd,
+ "no remote-host",
+ NO_STR "Disable GSMTAP Um logging\n")
+{
+ TALLOC_FREE(l23_cfg.gsmtap.remote_host);
+ if (vty->type != VTY_FILE)
+ vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_gsmtap_local_host,
+ cfg_gsmtap_gsmtap_local_host_cmd,
+ "local-host " VTY_IPV46_CMD,
+ "Set source for GSMTAP Um logging\n"
+ "Local IPv4 address\n" "Local IPv6 address\n")
+{
+ osmo_talloc_replace_string(l23_ctx, &l23_cfg.gsmtap.local_host, argv[0]);
+
+ if (vty->type != VTY_FILE)
+ vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_no_gsmtap_local_host,
+ cfg_gsmtap_no_gsmtap_local_host_cmd,
+ "no local-host",
+ NO_STR "Disable explicit source for GSMTAP Um logging\n")
+{
+ TALLOC_FREE(l23_cfg.gsmtap.local_host);
+ if (vty->type != VTY_FILE)
+ vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_gsmtap_lchan_all, cfg_gsmtap_gsmtap_lchan_all_cmd,
+ "lchan (enable-all|disable-all)",
+ "Enable/disable sending of UL/DL messages over GSMTAP\n"
+ "Enable all kinds of messages (all LCHAN)\n"
+ "Disable all kinds of messages (all LCHAN)\n")
+{
+ if (argv[0][0] == 'e') {
+ l23_cfg.gsmtap.lchan_mask = UINT32_MAX;
+ l23_cfg.gsmtap.lchan_acch_mask = UINT32_MAX;
+ l23_cfg.gsmtap.lchan_acch = true;
+ } else {
+ l23_cfg.gsmtap.lchan_mask = 0x00;
+ l23_cfg.gsmtap.lchan_acch_mask = 0x00;
+ l23_cfg.gsmtap.lchan_acch = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_gsmtap_lchan, cfg_gsmtap_gsmtap_lchan_cmd,
+ "HIDDEN", "HIDDEN")
+{
+ unsigned int channel;
+
+ if (osmo_str_startswith(argv[0], "sacch")) {
+ if (strcmp(argv[0], "sacch") == 0) {
+ l23_cfg.gsmtap.lchan_acch = true;
+ } else {
+ channel = get_string_value(gsmtap_gsm_channel_names, argv[0]);
+ channel &= ~GSMTAP_CHANNEL_ACCH;
+ l23_cfg.gsmtap.lchan_acch_mask |= (1 << channel);
+ }
+ } else {
+ channel = get_string_value(gsmtap_gsm_channel_names, argv[0]);
+ l23_cfg.gsmtap.lchan_mask |= (1 << channel);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_no_gsmtap_lchan, cfg_gsmtap_no_gsmtap_lchan_cmd,
+ "HIDDEN", "HIDDEN")
+{
+ unsigned int channel;
+
+ if (osmo_str_startswith(argv[0], "sacch")) {
+ if (strcmp(argv[0], "sacch") == 0) {
+ l23_cfg.gsmtap.lchan_acch = false;
+ } else {
+ channel = get_string_value(gsmtap_gsm_channel_names, argv[0]);
+ channel &= ~GSMTAP_CHANNEL_ACCH;
+ l23_cfg.gsmtap.lchan_acch_mask &= ~(1 << channel);
+ }
+ } else {
+ channel = get_string_value(gsmtap_gsm_channel_names, argv[0]);
+ l23_cfg.gsmtap.lchan_mask &= ~(1 << channel);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_gsmtap_categ_gprs_all, cfg_gsmtap_gsmtap_categ_gprs_all_cmd,
+ "category gprs (enable-all|disable-all)",
+ "Enable/disable sending of UL/DL messages over GSMTAP\n"
+ "Enable all kinds of messages (all categories)\n"
+ "Disable all kinds of messages (all categories)\n")
+{
+
+ if (strcmp(argv[0], "enable-all") == 0)
+ l23_cfg.gsmtap.categ_gprs_mask = UINT32_MAX;
+ else
+ l23_cfg.gsmtap.categ_gprs_mask = 0x00;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_gsmtap_categ_gprs, cfg_gsmtap_gsmtap_categ_gprs_cmd, "HIDDEN", "HIDDEN")
+{
+ int categ;
+
+ categ = get_string_value(gsmtap_categ_gprs_names, argv[0]);
+ if (categ < 0)
+ return CMD_WARNING;
+
+ l23_cfg.gsmtap.categ_gprs_mask |= (1 << categ);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gsmtap_no_gsmtap_categ_gprs, cfg_gsmtap_no_gsmtap_categ_gprs_cmd, "HIDDEN", "HIDDEN")
+{
+ int categ;
+
+ categ = get_string_value(gsmtap_categ_gprs_names, argv[0]);
+ if (categ < 0)
+ return CMD_WARNING;
+
+ l23_cfg.gsmtap.categ_gprs_mask &= ~(1 << categ);
+
+ return CMD_SUCCESS;
+}
+
+
+gDEFUN(l23_show_ms, l23_show_ms_cmd, "show ms [MS_NAME]",
+ SHOW_STR "Display available MS entities\n")
+{
+ struct osmocom_ms *ms;
+
+ if (argc) {
+ llist_for_each_entry(ms, &ms_list, entity) {
+ if (!strcmp(ms->name, argv[0])) {
+ l23_ms_dump(ms, vty);
+ return CMD_SUCCESS;
+ }
+ }
+ vty_out(vty, "MS name '%s' does not exist.%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(ms, &ms_list, entity) {
+ l23_ms_dump(ms, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int _sim_testcard_cmd(struct vty *vty, int argc, const char *argv[],
+ int attached)
+{
+ struct osmocom_ms *ms;
+ struct gsm_settings *set;
+ int rc;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (ms->subscr.sim_valid) {
+ vty_out(vty, "SIM already attached, remove first!%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ set = &ms->settings;
+ set->sim_type = GSM_SIM_TYPE_TEST;
+
+ if (argc == 2) {
+ vty_out(vty, "Give MNC together with MCC%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (argc >= 3) {
+ struct osmo_plmn_id plmn;
+ if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ memcpy(&set->test_sim.rplmn, &plmn, sizeof(plmn));
+ set->test_sim.rplmn_valid = true;
+ } else {
+ set->test_sim.rplmn_valid = false;
+ }
+
+ if (argc >= 4)
+ set->test_sim.lac = strtoul(argv[3], NULL, 16);
+
+ if (argc >= 5)
+ set->test_sim.tmsi = strtoul(argv[4], NULL, 16);
+
+ set->test_sim.imsi_attached = attached;
+
+ rc = gsm_subscr_insert(ms);
+ if (rc < 0) {
+ vty_out(vty, "Attach test SIM card failed: %d%s", rc, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_testcard, sim_testcard_cmd,
+ "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]",
+ "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n"
+ "Optionally set mobile Country Code of RPLMN\n"
+ "Optionally set mobile Network Code of RPLMN\n"
+ "Optionally set location area code of RPLMN\n"
+ "Optionally set current assigned TMSI")
+{
+ return _sim_testcard_cmd(vty, argc, argv, 0);
+}
+
+DEFUN(sim_testcard_att, sim_testcard_att_cmd,
+ "sim testcard MS_NAME MCC MNC LAC TMSI attached",
+ "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n"
+ "Set mobile Country Code of RPLMN\nSet mobile Network Code of RPLMN\n"
+ "Set location area code\nSet current assigned TMSI\n"
+ "Indicate to MM that card is already attached")
+{
+ return _sim_testcard_cmd(vty, argc, argv, 1);
+}
+
+DEFUN(sim_sap, sim_sap_cmd, "sim sap MS_NAME",
+ "SIM actions\nAttach SIM over SAP interface\n"
+ "Name of MS (see \"show ms\")\n")
+{
+ struct osmocom_ms *ms;
+ struct gsm_settings *set;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (ms->subscr.sim_valid) {
+ vty_out(vty, "SIM already attached, remove first!%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ set = &ms->settings;
+ set->sim_type = GSM_SIM_TYPE_SAP;
+ if (gsm_subscr_insert(ms) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME",
+ "SIM actions\nAttach SIM from reader\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+ struct gsm_settings *set;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (ms->subscr.sim_valid) {
+ vty_out(vty, "SIM already attached, remove first!%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ set = &ms->settings;
+ set->sim_type = GSM_SIM_TYPE_L1PHY;
+ gsm_subscr_insert(ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME",
+ "SIM actions\nDetach SIM card\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (!ms->subscr.sim_valid) {
+ vty_out(vty, "No SIM attached!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_remove(ms);
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_pin, sim_pin_cmd, "sim pin MS_NAME PIN",
+ "SIM actions\nEnter PIN for SIM card\nName of MS (see \"show ms\")\n"
+ "PIN number")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+ vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!ms->subscr.sim_pin_required) {
+ vty_out(vty, "No PIN is required at this time!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_sim_pin(ms, (char *)argv[1], "", 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_disable_pin, sim_disable_pin_cmd, "sim disable-pin MS_NAME PIN",
+ "SIM actions\nDisable PIN of SIM card\nName of MS (see \"show ms\")\n"
+ "PIN number")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+ vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_sim_pin(ms, (char *)argv[1], "", -1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_enable_pin, sim_enable_pin_cmd, "sim enable-pin MS_NAME PIN",
+ "SIM actions\nEnable PIN of SIM card\nName of MS (see \"show ms\")\n"
+ "PIN number")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+ vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_sim_pin(ms, (char *)argv[1], "", 1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_change_pin, sim_change_pin_cmd, "sim change-pin MS_NAME OLD NEW",
+ "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n"
+ "Old PIN number\nNew PIN number")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
+ vty_out(vty, "Old PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) {
+ vty_out(vty, "New PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 2);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW",
+ "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n"
+ "Personal Unblock Key\nNew PIN number")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (strlen(argv[1]) != 8) {
+ vty_out(vty, "PUC must be 8 digits!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) {
+ vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC",
+ "SIM actions\nChange LAI of SIM card\nName of MS (see \"show ms\")\n"
+ "Mobile Country Code\nMobile Network Code\nLocation Area Code "
+ " (use 0000 to remove LAI)")
+{
+ struct osmocom_ms *ms;
+ struct osmo_plmn_id plmn;
+ uint16_t lac;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ lac = strtoul(argv[3], NULL, 0);
+
+ memcpy(&ms->subscr.lai.plmn, &plmn, sizeof(plmn));
+ ms->subscr.lai.lac = lac;
+ ms->subscr.tmsi = GSM_RESERVED_TMSI;
+
+ gsm_subscr_write_loci(ms);
+
+ return CMD_SUCCESS;
+}
+
+/* per MS config */
+gDEFUN(l23_cfg_ms, l23_cfg_ms_cmd, "ms MS_NAME",
+ "Select a mobile station to configure\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ llist_for_each_entry(ms, &ms_list, entity) {
+ if (!strcmp(ms->name, argv[0])) {
+ vty->index = ms;
+ vty->node = MS_NODE;
+ return CMD_SUCCESS;
+ }
+ }
+
+ vty_out(vty, "MS name '%s' does not exits%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_ms_layer2, cfg_ms_layer2_cmd, "layer2-socket PATH",
+ "Define socket path to connect between layer 2 and layer 1\n"
+ "Unix socket, default '" L2_DEFAULT_SOCKET_PATH "'")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ OSMO_STRLCPY_ARRAY(set->layer2_socket_path, argv[0]);
+
+ l23_vty_restart_required_warn(vty, ms);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]",
+ "Set IMEI (enter without control digit)\n15 Digits IMEI\n"
+ "Software version digit")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+ char *error, *sv = "0";
+
+ if (argc >= 2)
+ sv = (char *)argv[1];
+
+ error = gsm_check_imei(argv[0], sv);
+ if (error) {
+ vty_out(vty, "%s%s", error, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ OSMO_STRLCPY_ARRAY(set->imei, argv[0]);
+ OSMO_STRLCPY_ARRAY(set->imeisv, argv[0]);
+ osmo_strlcpy(set->imeisv + GSM23003_IMEI_NUM_DIGITS, sv,
+ sizeof(set->imeisv) - GSM23003_IMEI_NUM_DIGITS);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed",
+ "Use fixed IMEI on every power on")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->imei_random = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>",
+ "Use random IMEI on every power on\n"
+ "Number of trailing digits to randomize")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->imei_random = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test|sap)",
+ "Set SIM card to attach when powering on\nAttach no SIM\n"
+ "Attach SIM from reader\nAttach build in test SIM\n"
+ "Attach SIM over SAP interface")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ switch (argv[0][0]) {
+ case 'n':
+ set->sim_type = GSM_SIM_TYPE_NONE;
+ break;
+ case 'r':
+ set->sim_type = GSM_SIM_TYPE_L1PHY;
+ break;
+ case 't':
+ set->sim_type = GSM_SIM_TYPE_TEST;
+ break;
+ case 's':
+ set->sim_type = GSM_SIM_TYPE_SAP;
+ break;
+ default:
+ vty_out(vty, "unknown SIM type%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown",
+ NO_STR "Activate and run MS")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ struct osmobb_l23_vty_sig_data data;
+ memset(&data, 0, sizeof(data));
+
+ data.vty = vty;
+ data.ms_start.ms = ms;
+ data.ms_start.rc = CMD_SUCCESS;
+ osmo_signal_dispatch(SS_L23_VTY, S_L23_VTY_MS_START, &data);
+
+ return data.ms_start.rc;
+}
+
+DEFUN(cfg_ms_shutdown, cfg_ms_shutdown_cmd, "shutdown",
+ "Shut down and deactivate MS")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ struct osmobb_l23_vty_sig_data data;
+ memset(&data, 0, sizeof(data));
+
+ data.vty = vty;
+ data.ms_stop.ms = ms;
+ data.ms_stop.force = false;
+ data.ms_stop.rc = CMD_SUCCESS;
+ osmo_signal_dispatch(SS_L23_VTY, S_L23_VTY_MS_STOP, &data);
+
+ return data.ms_stop.rc;
+}
+
+DEFUN(cfg_ms_shutdown_force, cfg_ms_shutdown_force_cmd, "shutdown force",
+ "Shut down and deactivate MS\nDo not perform IMSI detach")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ struct osmobb_l23_vty_sig_data data;
+ memset(&data, 0, sizeof(data));
+
+ data.vty = vty;
+ data.ms_stop.ms = ms;
+ data.ms_stop.force = true;
+ data.ms_stop.rc = CMD_SUCCESS;
+ osmo_signal_dispatch(SS_L23_VTY, S_L23_VTY_MS_STOP, &data);
+
+ return data.ms_stop.rc;
+}
+
+/* per testsim config */
+DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim",
+ "Configure test SIM emulation")
+{
+ vty->node = TESTSIM_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_imsi, cfg_testsim_imsi_cmd, "imsi IMSI",
+ "Set IMSI on test card\n15 digits IMSI")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ if (!osmo_imsi_str_valid(argv[0])) {
+ vty_out(vty, "Wrong IMSI format%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ OSMO_STRLCPY_ARRAY(set->test_sim.imsi, argv[0]);
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+#define HEX_STR "\nByte as two digits hexadecimal"
+DEFUN(cfg_testsim_ki_xor, cfg_testsim_ki_xor_cmd, "ki xor HEX HEX HEX HEX HEX HEX "
+ "HEX HEX HEX HEX HEX HEX",
+ "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR
+ HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR)
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+ uint8_t ki[12];
+ const char *p;
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ p = argv[i];
+ if (!strncmp(p, "0x", 2))
+ p += 2;
+ if (strlen(p) != 2) {
+ vty_out(vty, "Expecting two digits hex value (with or "
+ "without 0x in front)%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ki[i] = strtoul(p, NULL, 16);
+ }
+
+ set->test_sim.ki_type = OSMO_AUTH_ALG_XOR;
+ memcpy(set->test_sim.ki, ki, 12);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_ki_comp128, cfg_testsim_ki_comp128_cmd, "ki comp128 HEX HEX HEX "
+ "HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX",
+ "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR
+ HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR
+ HEX_STR HEX_STR HEX_STR HEX_STR)
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+ uint8_t ki[16];
+ const char *p;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ p = argv[i];
+ if (!strncmp(p, "0x", 2))
+ p += 2;
+ if (strlen(p) != 2) {
+ vty_out(vty, "Expecting two digits hex value (with or "
+ "without 0x in front)%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ki[i] = strtoul(p, NULL, 16);
+ }
+
+ set->test_sim.ki_type = OSMO_AUTH_ALG_COMP128v1;
+ memcpy(set->test_sim.ki, ki, 16);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_barr, cfg_testsim_barr_cmd, "barred-access",
+ "Allow access to barred cells")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->test_sim.barr = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_no_barr, cfg_testsim_no_barr_cmd, "no barred-access",
+ NO_STR "Deny access to barred cells")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->test_sim.barr = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_no_rplmn, cfg_testsim_no_rplmn_cmd, "no rplmn",
+ NO_STR "Unset Registered PLMN")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->test_sim.rplmn_valid = false;
+ set->test_sim.rplmn.mcc = 1;
+ set->test_sim.rplmn.mnc = 1;
+ set->test_sim.rplmn.mnc_3_digits = false;
+ set->test_sim.lac = 0x0000;
+ set->test_sim.tmsi = GSM_RESERVED_TMSI;
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+static int _testsim_rplmn_cmd(struct vty *vty, int argc, const char *argv[], bool attached)
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+ struct osmo_plmn_id plmn;
+
+ if (osmo_mcc_from_str(argv[0], &plmn.mcc) < 0) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (osmo_mnc_from_str(argv[1], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ set->test_sim.rplmn_valid = true;
+ memcpy(&set->test_sim.rplmn, &plmn, sizeof(plmn));
+
+ if (argc >= 3)
+ set->test_sim.lac = strtoul(argv[2], NULL, 16);
+ else
+ set->test_sim.lac = 0xfffe;
+
+ if (argc >= 4)
+ set->test_sim.tmsi = strtoul(argv[3], NULL, 16);
+ else
+ set->test_sim.tmsi = GSM_RESERVED_TMSI;
+
+ set->test_sim.imsi_attached = attached;
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_rplmn, cfg_testsim_rplmn_cmd,
+ "rplmn MCC MNC [LAC] [TMSI]",
+ "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n"
+ "Optionally set location area code\n"
+ "Optionally set current assigned TMSI")
+{
+ return _testsim_rplmn_cmd(vty, argc, argv, false);
+}
+
+DEFUN(cfg_testsim_rplmn_att, cfg_testsim_rplmn_att_cmd,
+ "rplmn MCC MNC LAC TMSI attached",
+ "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n"
+ "Set location area code\nSet current assigned TMSI\n"
+ "Indicate to MM that card is already attached")
+{
+ return _testsim_rplmn_cmd(vty, argc, argv, true);
+}
+
+DEFUN(cfg_testsim_hplmn, cfg_testsim_hplmn_cmd, "hplmn-search (everywhere|foreign-country)",
+ "Set Home PLMN search mode\n"
+ "Search for HPLMN when on any other network\n"
+ "Search for HPLMN when in a different country")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ switch (argv[0][0]) {
+ case 'e':
+ set->test_sim.always_search_hplmn = true;
+ break;
+ case 'f':
+ set->test_sim.always_search_hplmn = false;
+ break;
+ }
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+static int _testsim_locigprs_cmd(struct vty *vty, int argc, const char *argv[], bool attached)
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+ struct osmo_plmn_id plmn;
+
+ if (osmo_mcc_from_str(argv[0], &plmn.mcc) < 0) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (osmo_mnc_from_str(argv[1], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ set->test_sim.locigprs.valid = true;
+ set->test_sim.locigprs.rai.mcc = plmn.mcc;
+ set->test_sim.locigprs.rai.mnc = plmn.mnc;
+ set->test_sim.locigprs.rai.mnc_3_digits = plmn.mnc_3_digits;
+
+ if (argc >= 3)
+ set->test_sim.locigprs.rai.lac = strtoul(argv[2], NULL, 16);
+ else
+ set->test_sim.locigprs.rai.lac = 0xfffe;
+
+ if (argc >= 4)
+ set->test_sim.locigprs.rai.rac = strtoul(argv[3], NULL, 16);
+ else
+ set->test_sim.locigprs.rai.rac = 0xff;
+
+ if (argc >= 5)
+ set->test_sim.locigprs.ptmsi = strtoul(argv[4], NULL, 16);
+ else
+ set->test_sim.locigprs.ptmsi = GSM_RESERVED_TMSI;
+
+ if (argc >= 6)
+ set->test_sim.locigprs.ptmsi_sig = strtoul(argv[5], NULL, 16);
+ else
+ set->test_sim.locigprs.ptmsi_sig = GSM_RESERVED_TMSI;
+
+ set->test_sim.locigprs.imsi_attached = attached;
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_no_locigprs, cfg_testsim_no_locigprs_cmd, "no locigprs",
+ NO_STR "Unset EF LOCIgprs\n")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->test_sim.locigprs.valid = false;
+ set->test_sim.locigprs.ptmsi = GSM_RESERVED_TMSI;
+ set->test_sim.locigprs.ptmsi_sig = GSM_RESERVED_TMSI;
+ set->test_sim.locigprs.rai.mcc = 1;
+ set->test_sim.locigprs.rai.mnc = 1;
+ set->test_sim.locigprs.rai.mnc_3_digits = false;
+ set->test_sim.locigprs.rai.lac = 0x0000;
+ set->test_sim.locigprs.rai.rac = 0x0000;
+
+ l23_vty_restart_required_warn(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_testsim_locigprs, cfg_testsim_locigprs_cmd,
+ "locigprs MCC MNC [LAC] [RAC] [PTMSI] [PTMSISIG]",
+ "Set EF LOCIgprs\nMobile Country Code\nMobile Network Code\n"
+ "Optionally set location area code\n"
+ "Optionally set routing area code\n"
+ "Optionally set current assigned P-TMSI\n"
+ "Optionally set current assigned P-TMSI signature\n")
+{
+ return _testsim_locigprs_cmd(vty, argc, argv, false);
+}
+
+DEFUN(cfg_testsim_locigprs_att, cfg_testsim_locigprs_att_cmd,
+ "locigprs MCC MNC LAC RAC PTMSI PTMSISIG attached",
+ "Set EF LOCIgprs\nMobile Country Code\nMobile Network Code\n"
+ "Set location area code\n"
+ "Set routing area code\n"
+ "Set current assigned P-TMSI\n"
+ "Set current assigned P-TMSI signature\n"
+ "Indicate to MM that card is already attached\n")
+{
+ return _testsim_locigprs_cmd(vty, argc, argv, true);
+}
+
+static int l23_vty_config_write_gsmtap_node(struct vty *vty)
+{
+ const char *chan_buf;
+ unsigned int i;
+
+ vty_out(vty, "gsmtap%s", VTY_NEWLINE);
+
+ if (l23_cfg.gsmtap.remote_host)
+ vty_out(vty, " remote-host %s%s", l23_cfg.gsmtap.remote_host, VTY_NEWLINE);
+ else
+ vty_out(vty, " no remote-host%s", VTY_NEWLINE);
+
+ if (l23_cfg.gsmtap.local_host)
+ vty_out(vty, " local-host %s%s", l23_cfg.gsmtap.local_host, VTY_NEWLINE);
+ else
+ vty_out(vty, " no local-host%s", VTY_NEWLINE);
+
+ if (l23_cfg.gsmtap.lchan_acch)
+ vty_out(vty, " lchan sacch%s", VTY_NEWLINE);
+
+ for (i = 0; i < sizeof(uint32_t) * 8; i++) {
+ if (l23_cfg.gsmtap.lchan_acch_mask & ((uint32_t) 1 << i)) {
+ chan_buf = get_value_string_or_null(gsmtap_gsm_channel_names, GSMTAP_CHANNEL_ACCH | i);
+ if (chan_buf == NULL)
+ continue;
+ chan_buf = osmo_str_tolower(chan_buf);
+ vty_out(vty, " lchan %s%s", chan_buf, VTY_NEWLINE);
+ }
+ }
+
+ for (i = 0; i < sizeof(uint32_t) * 8; i++) {
+ if (l23_cfg.gsmtap.lchan_mask & ((uint32_t) 1 << i)) {
+ chan_buf = get_value_string_or_null(gsmtap_gsm_channel_names, i);
+ if (chan_buf == NULL)
+ continue;
+ chan_buf = osmo_str_tolower(chan_buf);
+ vty_out(vty, " lchan %s%s", chan_buf, VTY_NEWLINE);
+ }
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (l23_cfg.gsmtap.categ_gprs_mask & ((uint32_t)1 << i)) {
+ const char *category_buf;
+ if (!(category_buf = get_value_string_or_null(gsmtap_categ_gprs_names, i)))
+ continue;
+ vty_out(vty, " category gprs %s%s", category_buf, VTY_NEWLINE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int l23_vty_config_write_testsim_node(struct vty *vty, const struct osmocom_ms *ms, const char *prefix)
+{
+ const struct gsm_settings *set = &ms->settings;
+ vty_out(vty, "%stest-sim%s", prefix, VTY_NEWLINE);
+ vty_out(vty, "%s imsi %s%s", prefix, set->test_sim.imsi, VTY_NEWLINE);
+ switch (set->test_sim.ki_type) {
+ case OSMO_AUTH_ALG_XOR:
+ vty_out(vty, "%s ki xor %s%s",
+ prefix, osmo_hexdump(set->test_sim.ki, 12), VTY_NEWLINE);
+ break;
+ case OSMO_AUTH_ALG_COMP128v1:
+ vty_out(vty, "%s ki comp128 %s%s",
+ prefix, osmo_hexdump(set->test_sim.ki, 16), VTY_NEWLINE);
+ break;
+ }
+ if (!l23_vty_hide_default || set->test_sim.barr)
+ vty_out(vty, "%s %sbarred-access%s", prefix,
+ (set->test_sim.barr) ? "" : "no ", VTY_NEWLINE);
+ if (set->test_sim.rplmn_valid) {
+ vty_out(vty, "%s rplmn %s %s", prefix,
+ osmo_mcc_name(set->test_sim.rplmn.mcc),
+ osmo_mnc_name(set->test_sim.rplmn.mnc, set->test_sim.rplmn.mnc_3_digits));
+ if (set->test_sim.lac > 0x0000 && set->test_sim.lac < 0xfffe) {
+ vty_out(vty, " 0x%04x", set->test_sim.lac);
+ if (set->test_sim.tmsi != GSM_RESERVED_TMSI) {
+ vty_out(vty, " 0x%08x", set->test_sim.tmsi);
+ if (set->test_sim.imsi_attached)
+ vty_out(vty, " attached");
+ }
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ } else
+ if (!l23_vty_hide_default)
+ vty_out(vty, "%s no rplmn%s", prefix, VTY_NEWLINE);
+
+ if (!l23_vty_hide_default || set->test_sim.always_search_hplmn)
+ vty_out(vty, "%s hplmn-search %s%s", prefix,
+ set->test_sim.always_search_hplmn ? "everywhere" : "foreign-country",
+ VTY_NEWLINE);
+
+ if (set->test_sim.locigprs.valid) {
+ vty_out(vty, "%s locigprs %s %s", prefix,
+ osmo_mcc_name(set->test_sim.locigprs.rai.mcc),
+ osmo_mnc_name(set->test_sim.locigprs.rai.mnc,
+ set->test_sim.locigprs.rai.mnc_3_digits));
+ if (set->test_sim.locigprs.rai.lac > 0x0000 && set->test_sim.locigprs.rai.lac < 0xfffe) {
+ vty_out(vty, " 0x%04x", set->test_sim.locigprs.rai.lac);
+ if (set->test_sim.locigprs.rai.rac < 0xff) {
+ vty_out(vty, " 0x%02x", set->test_sim.locigprs.rai.rac);
+ if (set->test_sim.locigprs.ptmsi != GSM_RESERVED_TMSI) {
+ vty_out(vty, " 0x%08x 0x%06x",
+ set->test_sim.locigprs.ptmsi,
+ set->test_sim.locigprs.ptmsi_sig);
+ if (set->test_sim.locigprs.imsi_attached)
+ vty_out(vty, " attached");
+ }
+ }
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ } else
+ if (!l23_vty_hide_default)
+ vty_out(vty, "%s no locigprs%s", prefix, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+void l23_vty_config_write_ms_node(struct vty *vty, const struct osmocom_ms *ms, const char *prefix)
+{
+ size_t prefix_len = strlen(prefix);
+ char *prefix_content = alloca(prefix_len + 1 + 1);
+
+ memcpy(prefix_content, prefix, prefix_len);
+ prefix_content[prefix_len] = ' ';
+ prefix_content[prefix_len + 1] = '\0';
+
+ vty_out(vty, "%sms %s%s", prefix, ms->name, VTY_NEWLINE);
+ l23_vty_config_write_ms_node_contents(vty, ms, prefix_content);
+ l23_vty_config_write_ms_node_contents_final(vty, ms, prefix_content);
+}
+
+/* placeholder for shared VTY commands */
+void l23_vty_config_write_ms_node_contents(struct vty *vty, const struct osmocom_ms *ms, const char *prefix)
+{
+ const struct gsm_settings *set = &ms->settings;
+
+ vty_out(vty, "%slayer2-socket %s%s", prefix, set->layer2_socket_path,
+ VTY_NEWLINE);
+
+ vty_out(vty, "%simei %s %s%s", prefix, set->imei,
+ set->imeisv + strlen(set->imei), VTY_NEWLINE);
+ if (set->imei_random)
+ vty_out(vty, "%simei-random %d%s", prefix, set->imei_random, VTY_NEWLINE);
+ else if (!l23_vty_hide_default)
+ vty_out(vty, "%simei-fixed%s", prefix, VTY_NEWLINE);
+
+ switch (set->sim_type) {
+ case GSM_SIM_TYPE_NONE:
+ vty_out(vty, "%ssim none%s", prefix, VTY_NEWLINE);
+ break;
+ case GSM_SIM_TYPE_L1PHY:
+ vty_out(vty, "%ssim reader%s", prefix, VTY_NEWLINE);
+ break;
+ case GSM_SIM_TYPE_TEST:
+ vty_out(vty, "%ssim test%s", prefix, VTY_NEWLINE);
+ break;
+ case GSM_SIM_TYPE_SAP:
+ vty_out(vty, "%ssim sap%s", prefix, VTY_NEWLINE);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ l23_vty_config_write_testsim_node(vty, ms, prefix);
+}
+
+/* placeholder for shared VTY commands. Must be put at the end of the node: */
+void l23_vty_config_write_ms_node_contents_final(struct vty *vty, const struct osmocom_ms *ms, const char *prefix)
+{
+ /* no shutdown must be written to config, because shutdown is default */
+ vty_out(vty, "%s%sshutdown%s", prefix, (ms->shutdown != MS_SHUTDOWN_NONE) ? "" : "no ",
+ VTY_NEWLINE);
+ vty_out(vty, "!%s", VTY_NEWLINE);
+}
+
+static void l23_vty_init_gsmtap(void)
+{
+ cfg_gsmtap_gsmtap_lchan_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names,
+ "lchan (sacch|",
+ "|", ")", VTY_DO_LOWER);
+ cfg_gsmtap_gsmtap_lchan_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names,
+ "Enable sending of UL/DL messages over GSMTAP\n" "SACCH\n",
+ "\n", "", 0);
+
+ cfg_gsmtap_no_gsmtap_lchan_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names,
+ "no lchan (sacch|",
+ "|", ")", VTY_DO_LOWER);
+ cfg_gsmtap_no_gsmtap_lchan_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names,
+ NO_STR "Disable sending of UL/DL messages over GSMTAP\n" "SACCH\n",
+ "\n", "", 0);
+
+
+ cfg_gsmtap_gsmtap_categ_gprs_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_names,
+ "category gprs (",
+ "|", ")", VTY_DO_LOWER);
+ cfg_gsmtap_gsmtap_categ_gprs_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_help,
+ "GSMTAP Category\n" "GPRS\n",
+ "\n", "", 0);
+ cfg_gsmtap_no_gsmtap_categ_gprs_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_names,
+ "no category gprs (",
+ "|", ")", VTY_DO_LOWER);
+ cfg_gsmtap_no_gsmtap_categ_gprs_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_help,
+ NO_STR "GSMTAP Category\n" "GPRS\n",
+ "\n", "", 0);
+
+ install_element(CONFIG_NODE, &l23_cfg_gsmtap_cmd);
+
+ install_node(&gsmtap_node, l23_vty_config_write_gsmtap_node);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_remote_host_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_remote_host_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_local_host_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_local_host_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_lchan_all_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_lchan_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_lchan_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_categ_gprs_all_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_categ_gprs_cmd);
+ install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_categ_gprs_cmd);
+}
+
+int l23_vty_init(int (*config_write_ms_node_cb)(struct vty *), osmo_signal_cbfn *l23_vty_signal_cb)
+{
+ int rc = 0;
+
+ if (l23_app_info.opt_supported & L23_OPT_TAP)
+ l23_vty_init_gsmtap();
+
+ if (l23_app_info.opt_supported & L23_OPT_VTY)
+ osmo_stats_vty_add_cmds();
+
+ install_element_ve(&show_subscr_cmd);
+ install_element_ve(&show_support_cmd);
+
+ install_element(ENABLE_NODE, &sim_testcard_cmd);
+ install_element(ENABLE_NODE, &sim_testcard_att_cmd);
+ install_element(ENABLE_NODE, &sim_sap_cmd);
+ install_element(ENABLE_NODE, &sim_reader_cmd);
+ install_element(ENABLE_NODE, &sim_remove_cmd);
+ install_element(ENABLE_NODE, &sim_pin_cmd);
+ install_element(ENABLE_NODE, &sim_disable_pin_cmd);
+ install_element(ENABLE_NODE, &sim_enable_pin_cmd);
+ install_element(ENABLE_NODE, &sim_change_pin_cmd);
+ install_element(ENABLE_NODE, &sim_unblock_pin_cmd);
+ install_element(ENABLE_NODE, &sim_lai_cmd);
+
+ install_element(CONFIG_NODE, &cfg_hide_default_cmd);
+ install_element(CONFIG_NODE, &cfg_no_hide_default_cmd);
+
+ install_node(&ms_node, config_write_ms_node_cb);
+ install_element(MS_NODE, &cfg_ms_layer2_cmd);
+ install_element(MS_NODE, &cfg_ms_imei_cmd);
+ install_element(MS_NODE, &cfg_ms_imei_fixed_cmd);
+ install_element(MS_NODE, &cfg_ms_imei_random_cmd);
+ install_element(MS_NODE, &cfg_ms_sim_cmd);
+ install_element(MS_NODE, &cfg_ms_testsim_cmd);
+ install_node(&testsim_node, NULL);
+ install_element(TESTSIM_NODE, &cfg_testsim_imsi_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_ki_xor_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_ki_comp128_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_barr_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_no_barr_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_no_rplmn_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_rplmn_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_rplmn_att_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_hplmn_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_no_locigprs_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_locigprs_cmd);
+ install_element(TESTSIM_NODE, &cfg_testsim_locigprs_att_cmd);
+ install_element(MS_NODE, &cfg_ms_shutdown_cmd);
+ install_element(MS_NODE, &cfg_ms_shutdown_force_cmd);
+ install_element(MS_NODE, &cfg_ms_no_shutdown_cmd);
+
+ /* Register the talloc context introspection command */
+ osmo_talloc_vty_add_cmds();
+ osmo_cpu_sched_vty_init(l23_ctx);
+ if (l23_vty_signal_cb)
+ rc = osmo_signal_register_handler(SS_L23_VTY, l23_vty_signal_cb, NULL);
+ return rc;
+}
+
diff --git a/src/host/layer23/src/misc/Makefile.am b/src/host/layer23/src/misc/Makefile.am
index 78ab962f..05995c3f 100644
--- a/src/host/layer23/src/misc/Makefile.am
+++ b/src/host/layer23/src/misc/Makefile.am
@@ -1,14 +1,78 @@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS)
-LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBGPS_LIBS)
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
-bin_PROGRAMS = bcch_scan ccch_scan echo_test cell_log cbch_sniff
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOGPRSRLCMAC_CFLAGS) \
+ $(LIBOSMOGPRSLLC_CFLAGS) \
+ $(LIBOSMOGPRSSNDCP_CFLAGS) \
+ $(LIBGPS_CFLAGS) \
+ $(NULL)
-noinst_HEADERS = bcch_scan.h geo.h
+LDADD = \
+ $(top_builddir)/src/common/liblayer23.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOCODEC_LIBS) \
+ $(LIBOSMOGPRSRLCMAC_LIBS) \
+ $(LIBOSMOGPRSLLC_LIBS) \
+ $(LIBOSMOGPRSSNDCP_LIBS) \
+ $(LIBGPS_LIBS) \
+ $(NULL)
+
+bin_PROGRAMS = \
+ bcch_scan \
+ ccch_scan \
+ echo_test \
+ cell_log \
+ cbch_sniff \
+ gsmmap \
+ $(NULL)
+
+noinst_HEADERS = \
+ bcch_scan.h \
+ $(NULL)
+
+bcch_scan_SOURCES = \
+ $(top_srcdir)/src/common/main.c \
+ app_bcch_scan.c \
+ bcch_scan.c \
+ $(NULL)
+
+ccch_scan_SOURCES = \
+ $(top_srcdir)/src/common/main.c \
+ app_ccch_scan.c \
+ rslms.c \
+ $(NULL)
+
+echo_test_SOURCES = \
+ $(top_srcdir)/src/common/main.c \
+ app_echo_test.c \
+ $(NULL)
-bcch_scan_SOURCES = ../common/main.c app_bcch_scan.c bcch_scan.c
-ccch_scan_SOURCES = ../common/main.c app_ccch_scan.c rslms.c
-echo_test_SOURCES = ../common/main.c app_echo_test.c
cell_log_LDADD = $(LDADD) -lm
-cell_log_SOURCES = ../common/main.c app_cell_log.c cell_log.c geo.c
-cbch_sniff_SOURCES = ../common/main.c app_cbch_sniff.c
+cell_log_SOURCES = \
+ $(top_srcdir)/src/common/main.c \
+ app_cell_log.c \
+ cell_log.c \
+ geo.c \
+ $(NULL)
+
+cbch_sniff_SOURCES = \
+ $(top_srcdir)/src/common/main.c \
+ app_cbch_sniff.c \
+ $(NULL)
+
+gsmmap_LDADD = $(LDADD) -lm
+gsmmap_SOURCES = \
+ gsmmap.c \
+ geo.c \
+ locate.c \
+ log.c \
+ $(NULL)
diff --git a/src/host/layer23/src/misc/app_bcch_scan.c b/src/host/layer23/src/misc/app_bcch_scan.c
index 59466991..768920d0 100644
--- a/src/host/layer23/src/misc/app_bcch_scan.c
+++ b/src/host/layer23/src/misc/app_bcch_scan.c
@@ -15,16 +15,14 @@
* 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 <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/misc/layer3.h>
#include <osmocom/core/msgb.h>
@@ -35,6 +33,8 @@
#include <l1ctl_proto.h>
#include "bcch_scan.h"
+static struct osmocom_ms *g_ms;
+
static int signal_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
@@ -51,20 +51,32 @@ static int signal_cb(unsigned int subsys, unsigned int signal,
return 0;
}
-int l23_app_init(struct osmocom_ms *ms)
+static int _bcch_scan_start(void)
+{
+ int rc;
+
+ rc = layer2_open(g_ms, g_ms->settings.layer2_socket_path);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during layer2_open()\n");
+ return rc;
+ }
+
+ l1ctl_tx_reset_req(g_ms, L1CTL_RES_T_FULL);
+ return 0;
+}
+
+int l23_app_init(void)
{
+ g_ms = osmocom_ms_alloc(l23_ctx, "1");
+ OSMO_ASSERT(g_ms);
/* don't do layer3_init() as we don't want an actual L3 */
fps_init();
- l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
+ l23_app_start = _bcch_scan_start;
return osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
}
-static struct l23_app_info info = {
+const struct l23_app_info l23_app_info = {
.copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
.contribution = "Contributions by Holger Hans Peter Freyther\n",
+ .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_DBG,
};
-
-struct l23_app_info *l23_app_info()
-{
- return &info;
-}
diff --git a/src/host/layer23/src/misc/app_cbch_sniff.c b/src/host/layer23/src/misc/app_cbch_sniff.c
index ed85cefe..f83815ef 100644
--- a/src/host/layer23/src/misc/app_cbch_sniff.c
+++ b/src/host/layer23/src/misc/app_cbch_sniff.c
@@ -16,29 +16,29 @@
* 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 <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/misc/layer3.h>
+#include <osmocom/bb/common/sysinfo.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/signal.h>
#include <osmocom/gsm/rsl.h>
-
#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gsm/lapdm.h>
#include <l1ctl_proto.h>
-struct osmocom_ms *g_ms;
+static struct osmocom_ms *g_ms;
struct gsm48_sysinfo g_sysinfo = {};
static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s)
@@ -68,12 +68,12 @@ static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s)
return l1ctl_tx_dm_est_req_h1(ms,
s->maio, s->hsn, s->hopping, s->hopp_len,
chan_nr, s->tsc,
- GSM48_CMODE_SIGN, 0);
+ GSM48_CMODE_SIGN, 0, 0);
} else {
LOGP(DRR, LOGL_INFO, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n",
s->chan_nr, s->tsc, s->arfcn);
return l1ctl_tx_dm_est_req_h0(ms, s->arfcn,
- chan_nr, s->tsc, GSM48_CMODE_SIGN, 0);
+ chan_nr, s->tsc, GSM48_CMODE_SIGN, 0, 0);
}
}
@@ -115,14 +115,24 @@ static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
rllh->chan_nr, rllh->link_id);
- rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
return -EIO;
}
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
- rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, rllh->chan_nr);
+ return -EINVAL;
+ }
+
switch (ch_type) {
case RSL_CHAN_BCCH:
return bcch(ms, msg);
@@ -188,26 +198,37 @@ static int signal_cb(unsigned int subsys, unsigned int signal,
return 0;
}
-int l23_app_init(struct osmocom_ms *ms)
+static int _cbch_sniff_start(void)
{
- /* don't do layer3_init() as we don't want an actual L3 */
+ int rc;
- g_ms = ms;
- lapdm_channel_set_l3(&ms->lapdm_channel, &rcv_rsl, ms);
+ rc = layer2_open(g_ms, g_ms->settings.layer2_socket_path);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during layer2_open()\n");
+ return rc;
+ }
- l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
+ l1ctl_tx_reset_req(g_ms, L1CTL_RES_T_FULL);
/* FIXME: L1CTL_RES_T_FULL doesn't reset dedicated mode
* (if previously set), so we release it here. */
- l1ctl_tx_dm_rel_req(ms);
+ l1ctl_tx_dm_rel_req(g_ms);
+ return 0;
+}
+
+int l23_app_init(void)
+{
+ /* don't do layer3_init() as we don't want an actual L3 */
+ l23_app_start = _cbch_sniff_start;
+
+ g_ms = osmocom_ms_alloc(l23_ctx, "1");
+ OSMO_ASSERT(g_ms);
+
+ lapdm_channel_set_l3(&g_ms->lapdm_channel, &rcv_rsl, g_ms);
return osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
}
-static struct l23_app_info info = {
+const struct l23_app_info l23_app_info = {
.copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
.contribution = "Contributions by Holger Hans Peter Freyther\n",
+ .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_DBG,
};
-
-struct l23_app_info *l23_app_info()
-{
- return &info;
-}
diff --git a/src/host/layer23/src/misc/app_ccch_scan.c b/src/host/layer23/src/misc/app_ccch_scan.c
index e5a184f1..d2b6c242 100644
--- a/src/host/layer23/src/misc/app_ccch_scan.c
+++ b/src/host/layer23/src/misc/app_ccch_scan.c
@@ -14,10 +14,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 <stdint.h>
@@ -36,12 +32,16 @@
#include <osmocom/bb/misc/rslms.h>
#include <osmocom/bb/misc/layer3.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/ms.h>
#include <l1ctl_proto.h>
static struct {
+ struct osmocom_ms *ms;
int ccch_mode;
} app_state;
@@ -170,7 +170,12 @@ static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms)
if (ia->page_mode & 0xf0)
return 0;
- rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, ia->chan_desc.chan_nr);
+ return -EINVAL;
+ }
if (!ia->chan_desc.h0.h) {
/* Non-hopping */
@@ -231,24 +236,6 @@ static char *chan_need(int need)
}
}
-static char *mi_type_to_string(int type)
-{
- switch (type) {
- case GSM_MI_TYPE_NONE:
- return "none";
- case GSM_MI_TYPE_IMSI:
- return "imsi";
- case GSM_MI_TYPE_IMEI:
- return "imei";
- case GSM_MI_TYPE_IMEISV:
- return "imeisv";
- case GSM_MI_TYPE_TMSI:
- return "tmsi";
- default:
- return "invalid";
- }
-}
-
/**
* This can contain two MIs. The size checking is a bit of a mess.
*/
@@ -256,6 +243,7 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms)
{
struct gsm48_paging1 *pag;
int len1, len2, mi_type, tag;
+ struct osmo_mobile_identity mi;
char mi_string[GSM48_MI_SIZE];
/* is there enough room for the header + LV? */
@@ -274,11 +262,11 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms)
}
if (mi_type != GSM_MI_TYPE_NONE) {
- gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[1], len1);
- LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to %s M(%s) \n",
+ osmo_mobile_identity_decode(&mi, &pag->data[1], len1, false);
+ osmo_mobile_identity_to_str_buf(mi_string, sizeof(mi_string), &mi);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to M(%s)\n",
pag_print_mode(pag->pag_mode),
chan_need(pag->cneed1),
- mi_type_to_string(mi_type),
mi_string);
}
@@ -295,11 +283,11 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms)
return -1;
}
- gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2 + len1 + 2], len2);
- LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to %s M(%s) \n",
+ osmo_mobile_identity_decode(&mi, &pag->data[2 + len1 + 2], len2, false);
+ osmo_mobile_identity_to_str_buf(mi_string, sizeof(mi_string), &mi);
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to M(%s)\n",
pag_print_mode(pag->pag_mode),
chan_need(pag->cneed2),
- mi_type_to_string(mi_type),
mi_string);
}
return 0;
@@ -308,8 +296,9 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms)
static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms)
{
struct gsm48_paging2 *pag;
- int tag, len, mi_type;
+ struct osmo_mobile_identity mi;
char mi_string[GSM48_MI_SIZE];
+ int tag, len;
if (msgb_l3len(msg) < sizeof(*pag)) {
LOGP(DRR, LOGL_ERROR, "Paging2 message is too small.\n");
@@ -317,12 +306,14 @@ static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms)
}
pag = msgb_l3(msg);
- LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n",
- pag_print_mode(pag->pag_mode),
- chan_need(pag->cneed1), pag->tmsi1);
- LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n",
- pag_print_mode(pag->pag_mode),
- chan_need(pag->cneed2), pag->tmsi2);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to M(TMSI-0x%08x)\n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1),
+ osmo_load32be(&pag->tmsi1));
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to M(TMSI-0x%08x)\n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed2),
+ osmo_load32be(&pag->tmsi2));
/* no optional element */
if (msgb_l3len(msg) < sizeof(*pag) + 3)
@@ -330,7 +321,6 @@ static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms)
tag = pag->data[0];
len = pag->data[1];
- mi_type = pag->data[2] & GSM_MI_TYPE_MASK;
if (tag != GSM48_IE_MOBILE_ID)
return 0;
@@ -340,11 +330,10 @@ static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms)
return -1;
}
- gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2], len);
- LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to %s M(%s) \n",
+ osmo_mobile_identity_decode(&mi, &pag->data[2], len, false);
+ osmo_mobile_identity_to_str_buf(mi_string, sizeof(mi_string), &mi);
+ LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan n/a to M(%s)\n",
pag_print_mode(pag->pag_mode),
- "n/a ",
- mi_type_to_string(mi_type),
mi_string);
return 0;
@@ -360,18 +349,20 @@ static int gsm48_rx_paging_p3(struct msgb *msg, struct osmocom_ms *ms)
}
pag = msgb_l3(msg);
- LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n",
- pag_print_mode(pag->pag_mode),
- chan_need(pag->cneed1), pag->tmsi1);
- LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n",
- pag_print_mode(pag->pag_mode),
- chan_need(pag->cneed2), pag->tmsi2);
- LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to TMSI M(0x%x) \n",
- pag_print_mode(pag->pag_mode),
- "n/a ", pag->tmsi3);
- LOGP(DRR, LOGL_NOTICE, "Paging4: %s chan %s to TMSI M(0x%x) \n",
- pag_print_mode(pag->pag_mode),
- "n/a ", pag->tmsi4);
+ LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to M(TMSI-0x%08x)\n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed1),
+ osmo_load32be(&pag->tmsi1));
+ LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to M(TMSI-0x%08x)\n",
+ pag_print_mode(pag->pag_mode),
+ chan_need(pag->cneed2),
+ osmo_load32be(&pag->tmsi2));
+ LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan n/a to M(TMSI-0x%08x)\n",
+ pag_print_mode(pag->pag_mode),
+ osmo_load32be(&pag->tmsi3));
+ LOGP(DRR, LOGL_NOTICE, "Paging4: %s chan n/a to M(TMSI-0x%08x)\n",
+ pag_print_mode(pag->pag_mode),
+ osmo_load32be(&pag->tmsi4));
return 0;
}
@@ -489,20 +480,33 @@ static int signal_cb(unsigned int subsys, unsigned int signal,
return 0;
}
+static int _ccch_scan_start(void)
+{
+ int rc;
+
+ rc = layer2_open(app_state.ms, app_state.ms->settings.layer2_socket_path);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during layer2_open()\n");
+ return rc;
+ }
+
+ l1ctl_tx_reset_req(app_state.ms, L1CTL_RES_T_FULL);
+ return 0;
+}
-int l23_app_init(struct osmocom_ms *ms)
+int l23_app_init(void)
{
+ l23_app_start = _ccch_scan_start;
+
+ app_state.ms = osmocom_ms_alloc(l23_ctx, "1");
+ OSMO_ASSERT(app_state.ms);
+
osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
- l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
- return layer3_init(ms);
+ return layer3_init(app_state.ms);
}
-static struct l23_app_info info = {
+const struct l23_app_info l23_app_info = {
.copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
.contribution = "Contributions by Holger Hans Peter Freyther\n",
+ .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_DBG,
};
-
-struct l23_app_info *l23_app_info()
-{
- return &info;
-}
diff --git a/src/host/layer23/src/misc/app_cell_log.c b/src/host/layer23/src/misc/app_cell_log.c
index 5b7f7b40..9c498006 100644
--- a/src/host/layer23/src/misc/app_cell_log.c
+++ b/src/host/layer23/src/misc/app_cell_log.c
@@ -14,10 +14,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 <signal.h>
@@ -30,28 +26,44 @@
#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/gps.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/misc/cell_log.h>
+#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <l1ctl_proto.h>
-extern struct log_target *stderr_target;
-extern void *l23_ctx;
-
extern uint16_t basic_band_range[][2];
extern uint16_t (*band_range)[][2];
char *logname = "/dev/null";
int RACH_MAX = 2;
+static struct osmocom_ms *g_ms;
+
-int _scan_work(struct osmocom_ms *ms)
+int _scan_start(void)
{
+ int rc;
+
+ rc = layer2_open(g_ms, g_ms->settings.layer2_socket_path);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during layer2_open()\n");
+ return rc;
+ }
+
+ l1ctl_tx_reset_req(g_ms, L1CTL_RES_T_FULL);
return 0;
}
-int _scan_exit(struct osmocom_ms *ms)
+int _scan_work(void)
+{
+ return 0;
+}
+
+int _scan_exit(void)
{
/* in case there is a lockup during exit */
signal(SIGINT, SIG_DFL);
@@ -64,33 +76,31 @@ int _scan_exit(struct osmocom_ms *ms)
return 0;
}
-int l23_app_init(struct osmocom_ms *ms)
+int l23_app_init(void)
{
int rc;
srand(time(NULL));
-// log_parse_category_mask(stderr_target, "DL1C:DRSL:DRR:DGPS:DSUM");
- log_parse_category_mask(stderr_target, "DSUM");
- log_set_log_level(stderr_target, LOGL_INFO);
+// log_parse_category_mask(osmo_stderr_target, "DL1C:DRSL:DRR:DGPS:DSUM");
+ log_parse_category_mask(osmo_stderr_target, "DSUM");
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+ l23_app_start = _scan_start;
l23_app_work = _scan_work;
l23_app_exit = _scan_exit;
- rc = scan_init(ms);
+ g_ms = osmocom_ms_alloc(l23_ctx, "1");
+ OSMO_ASSERT(g_ms);
+
+ rc = scan_init(g_ms);
if (rc)
return rc;
- l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
printf("Mobile initialized, please start phone now!\n");
return 0;
}
-static int l23_cfg_supported()
-{
- return L23_OPT_TAP | L23_OPT_DBG;
-}
-
static int l23_getopt_options(struct option **options)
{
static struct option opts [] = {
@@ -151,7 +161,7 @@ static void parse_band_range(char* s)
(*band_range)[i][1] = 0;
}
-static int l23_cfg_print_help()
+static int l23_cfg_print_help(void)
{
printf("\nApplication specific\n");
printf(" -l --logfile LOGFILE Logfile for the cell log.\n");
@@ -232,16 +242,11 @@ cmd_line_error:
exit(1);
}
-static struct l23_app_info info = {
+const struct l23_app_info l23_app_info = {
.copyright = "Copyright (C) 2010 Andreas Eversberg\n",
.getopt_string = "g:p:l:r:nf:b:A:",
- .cfg_supported = l23_cfg_supported,
+ .opt_supported = L23_OPT_TAP | L23_OPT_DBG,
.cfg_getopt_opt = l23_getopt_options,
.cfg_handle_opt = l23_cfg_handle,
.cfg_print_help = l23_cfg_print_help,
};
-
-struct l23_app_info *l23_app_info()
-{
- return &info;
-}
diff --git a/src/host/layer23/src/misc/app_echo_test.c b/src/host/layer23/src/misc/app_echo_test.c
index 3a0ee0f4..f5b3136b 100644
--- a/src/host/layer23/src/misc/app_echo_test.c
+++ b/src/host/layer23/src/misc/app_echo_test.c
@@ -15,22 +15,19 @@
* 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 <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/misc/layer3.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
-
+#include <osmocom/core/timer.h>
static struct {
struct osmo_timer_list timer;
@@ -44,8 +41,11 @@ static void test_tmr_cb(void *data)
osmo_timer_schedule(&test_data.timer, 1, 0);
}
-int l23_app_init(struct osmocom_ms *ms)
+int l23_app_init(void)
{
+ struct osmocom_ms *ms = osmocom_ms_alloc(l23_ctx, "1");
+ OSMO_ASSERT(ms);
+
test_data.timer.cb = &test_tmr_cb;
test_data.timer.data = ms;
@@ -54,12 +54,7 @@ int l23_app_init(struct osmocom_ms *ms)
return 0;
}
-static struct l23_app_info info = {
+const struct l23_app_info l23_app_info = {
.copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n",
.contribution = "Contributions by Holger Hans Peter Freyther\n",
};
-
-struct l23_app_info *l23_app_info()
-{
- return &info;
-}
diff --git a/src/host/layer23/src/misc/bcch_scan.c b/src/host/layer23/src/misc/bcch_scan.c
index 5dc0bc3b..69df043d 100644
--- a/src/host/layer23/src/misc/bcch_scan.c
+++ b/src/host/layer23/src/misc/bcch_scan.c
@@ -14,10 +14,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>
diff --git a/src/host/layer23/src/misc/cell_log.c b/src/host/layer23/src/misc/cell_log.c
index 9edd742e..ed4d74d1 100644
--- a/src/host/layer23/src/misc/cell_log.c
+++ b/src/host/layer23/src/misc/cell_log.c
@@ -14,10 +14,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>
@@ -36,14 +32,19 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/lapdm.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/gps.h>
+#include <osmocom/bb/common/sysinfo.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
#include <osmocom/bb/misc/cell_log.h>
-#include "../../../gsmmap/geo.h"
+#include <osmocom/bb/misc/geo.h>
#define READ_WAIT 2, 0
#define RACH_WAIT 0, 900000
@@ -97,7 +98,7 @@ static struct log_si {
uint16_t flags;
uint8_t bsic;
int8_t rxlev_dbm;
- uint16_t mcc, mnc, lac, cellid;
+ struct osmo_cell_global_id cgi;
uint8_t ta;
double latitude, longitude;
} log_si;
@@ -186,9 +187,10 @@ static void log_sysinfo(void)
if (log_si.ta != 0xff)
sprintf(ta_str, " TA=%d", log_si.ta);
- LOGP(DSUM, LOGL_INFO, "Cell: ARFCN=%d MCC=%s MNC=%s (%s, %s)%s\n",
- arfcn, gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc),
- gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc), ta_str);
+ LOGP(DSUM, LOGL_INFO, "Cell: ARFCN=%d MCC-MNC=%s (%s, %s)%s\n",
+ arfcn, osmo_plmn_name(&s->lai.plmn),
+ gsm_get_mcc(s->lai.plmn.mcc),
+ gsm_get_mnc(&s->lai.plmn), ta_str);
LOGFILE("[sysinfo]\n");
LOGFILE("arfcn %d\n", s->arfcn);
@@ -291,7 +293,7 @@ static void start_rach(void)
ncch->data[2] = (s->ccch_conf == 1) << 7;
ncch->data[3] = 0;
ncch->data[4] = RSL_IE_ACCESS_DELAY;
- ncch->data[5] = 0; /* no delay */
+ ncch->data[5] = 0; /* no delay */
ncch->data[6] = RSL_IE_MS_POWER;
ncch->data[7] = 0; /* full power */
@@ -669,11 +671,15 @@ static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
struct tlv_parsed tv;
uint8_t ch_type, ch_subch, ch_ts;
-
+
DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
rllh->chan_nr, rllh->link_id);
- rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
return -EIO;
@@ -684,7 +690,13 @@ static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, rllh->chan_nr);
+ return -EINVAL;
+ }
+
switch (ch_type) {
case RSL_CHAN_PCH_AGCH:
return pch_agch(ms, msg);
diff --git a/src/host/layer23/src/misc/geo.c b/src/host/layer23/src/misc/geo.c
index 65633d2c..e02a2391 100644
--- a/src/host/layer23/src/misc/geo.c
+++ b/src/host/layer23/src/misc/geo.c
@@ -1,5 +1,6 @@
#include <math.h>
-#include "geo.h"
+
+#include <osmocom/bb/misc/geo.h>
void geo2space(double *x, double *y, double *z, double lon, double lat)
{
diff --git a/src/host/layer23/src/misc/geo.h b/src/host/layer23/src/misc/geo.h
deleted file mode 100644
index 25e26cba..00000000
--- a/src/host/layer23/src/misc/geo.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* WGS 84 */
-#define EQUATOR_RADIUS 6378137.0
-#define POLE_RADIUS 6356752.314
-
-#define PI 3.1415926536
-
-void geo2space(double *x, double *y, double *z, double lat, double lon);
-void space2geo(double *lat, double *lon, double x, double y, double z);
-double distinspace(double x1, double y1, double z1, double x2, double y2,
- double z2);
-double distonplane(double x1, double y1, double x2, double y2);
-
diff --git a/src/host/gsmmap/gsmmap.c b/src/host/layer23/src/misc/gsmmap.c
index 83f0d01c..378b358b 100644
--- a/src/host/gsmmap/gsmmap.c
+++ b/src/host/layer23/src/misc/gsmmap.c
@@ -14,10 +14,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.
- *
*/
#warning todo bsic
#include <stdio.h>
@@ -34,9 +30,9 @@
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/logging.h>
-#include "log.h"
-#include "geo.h"
-#include "locate.h"
+#include <osmocom/bb/misc/log.h>
+#include <osmocom/bb/misc/geo.h>
+#include <osmocom/bb/misc/locate.h>
/*
* structure of power and cell infos
@@ -56,7 +52,7 @@ static void nomem(void)
exit(-ENOMEM);
}
-static void add_power()
+static void add_power(void)
{
struct node_power *node_power;
@@ -85,7 +81,7 @@ static void print_si(void *priv, const char *fmt, ...)
fprintf(outfp, "%s", buffer);
}
-static void add_sysinfo()
+static void add_sysinfo(void)
{
struct gsm48_sysinfo s;
struct node_mcc *mcc;
@@ -125,13 +121,13 @@ static void add_sysinfo()
23);
printf("--------------------------------------------------------------------------\n");
gsm48_sysinfo_dump(&s, sysinfo.arfcn, print_si, stdout, NULL);
- mcc = get_node_mcc(s.mcc);
+ mcc = get_node_mcc(s.lai.plmn.mcc);
if (!mcc)
nomem();
- mnc = get_node_mnc(mcc, s.mnc);
+ mnc = get_node_mnc(mcc, s.lai.plmn.mnc, s.lai.plmn.mnc_3_digits);
if (!mnc)
nomem();
- lac = get_node_lac(mnc, s.lac);
+ lac = get_node_lac(mnc, s.lai.lac);
if (!lac)
nomem();
cell = get_node_cell(lac, s.cell_id);
@@ -300,8 +296,7 @@ void kml_footer(FILE *outfp)
}
-void kml_meas(FILE *outfp, struct node_meas *meas, int n, uint16_t mcc,
- uint16_t mnc, uint16_t lac, uint16_t cellid)
+static void kml_meas(FILE *outfp, struct node_meas *meas, int n, const struct osmo_cell_global_id *cgi)
{
struct tm *tm = localtime(&meas->gmt);
@@ -309,8 +304,11 @@ void kml_meas(FILE *outfp, struct node_meas *meas, int n, uint16_t mcc,
fprintf(outfp, "\t\t\t\t\t\t<name>%d: %d</name>\n", n, meas->rxlev);
fprintf(outfp, "\t\t\t\t\t\t<description>\n");
fprintf(outfp, "MCC=%s MNC=%s\nLAC=%04x CELL-ID=%04x\n(%s %s)\n",
- gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac, cellid,
- gsm_get_mcc(mcc), gsm_get_mnc(mcc, mnc));
+ 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,
+ gsm_get_mcc(cgi->lai.plmn.mcc),
+ gsm_get_mnc(&cgi->lai.plmn));
fprintf(outfp, "\n%s", asctime(tm));
fprintf(outfp, "RX-LEV %d dBm\n", meas->rxlev);
if (meas->ta_valid)
@@ -427,11 +425,11 @@ void kml_cell(FILE *outfp, struct node_cell *cell)
return;
fprintf(outfp, "\t\t\t\t\t<Placemark>\n");
- fprintf(outfp, "\t\t\t\t\t\t<name>MCC=%s MNC=%s\nLAC=%04x "
- "CELL-ID=%04x\n(%s %s)</name>\n", gsm_print_mcc(cell->s.mcc),
- gsm_print_mnc(cell->s.mnc), cell->s.lac, cell->s.cell_id,
- gsm_get_mcc(cell->s.mcc),
- gsm_get_mnc(cell->s.mcc, cell->s.mnc));
+ fprintf(outfp, "\t\t\t\t\t\t<name>LAI=%s "
+ "CELL-ID=%04x\n(%s %s)</name>\n",
+ osmo_lai_name(&cell->s.lai), cell->s.cell_id,
+ gsm_get_mcc(cell->s.lai.plmn.mcc),
+ gsm_get_mnc(&cell->s.lai.plmn));
fprintf(outfp, "\t\t\t\t\t\t<description>\n");
gsm48_sysinfo_dump(&cell->s, cell->sysinfo.arfcn, print_si, outfp,
NULL);
@@ -580,15 +578,20 @@ usage:
/* folder open */
fprintf(outfp, "\t<Folder>\n");
fprintf(outfp, "\t\t<name>MCC %s (%s)</name>\n",
- gsm_print_mcc(mcc->mcc), gsm_get_mcc(mcc->mcc));
+ osmo_mcc_name(mcc->mcc), gsm_get_mcc(mcc->mcc));
fprintf(outfp, "\t\t<open>0</open>\n");
mnc = mcc->mnc;
while (mnc) {
+ struct osmo_plmn_id plmn = {
+ .mcc = mcc->mcc,
+ .mnc = mnc->mnc,
+ .mnc_3_digits = mnc->mnc_3_digits,
+ };
printf(" MNC: %02x\n", mnc->mnc);
/* folder open */
fprintf(outfp, "\t\t<Folder>\n");
fprintf(outfp, "\t\t\t<name>MNC %s (%s)</name>\n",
- gsm_print_mnc(mnc->mnc), gsm_get_mnc(mcc->mcc, mnc->mnc));
+ osmo_mnc_name(mnc->mnc, mnc->mnc_3_digits), gsm_get_mnc(&plmn));
fprintf(outfp, "\t\t\t<open>0</open>\n");
lac = mnc->lac;
while (lac) {
@@ -608,9 +611,20 @@ usage:
while (meas) {
if (meas->ta_valid)
printf(" TA: %d\n", meas->ta);
- if (meas->gps_valid)
- kml_meas(outfp, meas, ++n, mcc->mcc, mnc->mnc,
- lac->lac, cell->cellid);
+ if (meas->gps_valid) {
+ struct osmo_cell_global_id cgi = {
+ .lai = {
+ .plmn = {
+ .mcc = mcc->mcc,
+ .mnc = mnc->mnc,
+ .mnc_3_digits = mnc->mnc_3_digits,
+ },
+ .lac = lac->lac,
+ },
+ .cell_identity = cell->cellid,
+ };
+ kml_meas(outfp, meas, ++n, &cgi);
+ }
meas = meas->next;
}
kml_cell(outfp, cell);
diff --git a/src/host/gsmmap/locate.c b/src/host/layer23/src/misc/locate.c
index ed0ac931..eb37285e 100644
--- a/src/host/gsmmap/locate.c
+++ b/src/host/layer23/src/misc/locate.c
@@ -5,8 +5,8 @@
#include <errno.h>
#include <math.h>
-#include "geo.h"
-#include "locate.h"
+#include <osmocom/bb/misc/geo.h>
+#include <osmocom/bb/misc/locate.h>
#define CIRCLE_PROBE 30.0
#define FINETUNE_RADIUS 5.0
diff --git a/src/host/gsmmap/log.c b/src/host/layer23/src/misc/log.c
index c2db801e..577e1268 100644
--- a/src/host/gsmmap/log.c
+++ b/src/host/layer23/src/misc/log.c
@@ -14,18 +14,13 @@
* 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>
#include <stdlib.h>
#include <osmocom/bb/common/osmocom_data.h>
-
-#include "log.h"
+#include <osmocom/bb/misc/log.h>
extern struct power power;
extern struct sysinfo sysinfo;
@@ -60,18 +55,22 @@ struct node_mcc *get_node_mcc(uint16_t mcc)
return node_mcc;
}
-struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc)
+struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc, bool mnc_3_digits)
{
struct node_mnc *node_mnc;
struct node_mnc **node_mnc_p = &mcc->mnc;
while (*node_mnc_p) {
/* found in list */
- if ((*node_mnc_p)->mnc == mnc)
+ if ((*node_mnc_p)->mnc == mnc &&
+ (*node_mnc_p)->mnc_3_digits == mnc_3_digits)
return *node_mnc_p;
/* insert into list */
- if ((*node_mnc_p)->mnc > mnc)
+ if (((*node_mnc_p)->mnc > mnc) ||
+ ((*node_mnc_p)->mnc == mnc &&
+ (*node_mnc_p)->mnc_3_digits > mnc_3_digits)) {
break;
+ }
node_mnc_p = &((*node_mnc_p)->next);
}
@@ -80,6 +79,7 @@ struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc)
if (!node_mnc)
return NULL;
node_mnc->mnc = mnc;
+ node_mnc->mnc_3_digits = mnc_3_digits;
node_mnc->next = *node_mnc_p;
*node_mnc_p = node_mnc;
return node_mnc;
diff --git a/src/host/layer23/src/misc/rslms.c b/src/host/layer23/src/misc/rslms.c
index 455e6631..c071113b 100644
--- a/src/host/layer23/src/misc/rslms.c
+++ b/src/host/layer23/src/misc/rslms.c
@@ -14,10 +14,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 <stdint.h>
@@ -34,6 +30,7 @@
#include <osmocom/bb/misc/rslms.h>
#include <osmocom/bb/misc/layer3.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1ctl.h>
/* Send a 'simple' RLL request to L2 */
@@ -61,11 +58,15 @@ static int rslms_rx_udata_ind(struct msgb *msg, struct osmocom_ms *ms)
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
struct tlv_parsed tv;
int rc = 0;
-
+
DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
rllh->chan_nr, rllh->link_id);
- rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
return -EIO;
diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am
index 783ae162..d00e45ea 100644
--- a/src/host/layer23/src/mobile/Makefile.am
+++ b/src/host/layer23/src/mobile/Makefile.am
@@ -1,16 +1,62 @@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS) $(LIBLUA_CFLAGS)
-LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBGPS_LIBS) $(LIBLUA_LIBS)
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOISDN_CFLAGS) \
+ $(LIBOSMOGPRSRLCMAC_CFLAGS) \
+ $(LIBOSMOGPRSLLC_CFLAGS) \
+ $(LIBOSMOGPRSSNDCP_CFLAGS) \
+ $(LIBOSMOCODEC_CFLAGS) \
+ $(LIBOSMOGAPK_CFLAGS) \
+ $(LIBGPS_CFLAGS) \
+ $(LIBLUA_CFLAGS) \
+ $(NULL)
noinst_LIBRARIES = libmobile.a
-libmobile_a_SOURCES = gsm322.c gsm480_ss.c gsm411_sms.c gsm48_cc.c gsm48_mm.c \
- gsm48_rr.c gsm414.c mnccms.c settings.c subscriber.c support.c \
- transaction.c vty_interface.c voice.c mncc_sock.c primitives.c
+libmobile_a_SOURCES = \
+ gsm322.c \
+ gsm480_ss.c \
+ gsm411_sms.c \
+ gsm48_cc.c \
+ gsm44068_gcc_bcc.c \
+ gsm48_mm.c \
+ gsm48_rr.c \
+ gsm414.c \
+ mnccms.c \
+ mncc_sock.c \
+ primitives.c \
+ tch.c \
+ tch_data.c \
+ tch_data_sock.c \
+ tch_voice.c \
+ transaction.c \
+ vty_interface.c \
+ $(NULL)
bin_PROGRAMS = mobile
mobile_SOURCES = main.c app_mobile.c
-mobile_LDADD = libmobile.a $(LDADD)
+mobile_LDADD = \
+ libmobile.a \
+ $(top_builddir)/src/common/liblayer23.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOISDN_LIBS) \
+ $(LIBOSMOGPRSRLCMAC_LIBS) \
+ $(LIBOSMOGPRSLLC_LIBS) \
+ $(LIBOSMOGPRSSNDCP_LIBS) \
+ $(LIBOSMOCODEC_LIBS) \
+ $(LIBOSMOGAPK_LIBS) \
+ $(LIBGPS_LIBS) \
+ $(LIBLUA_LIBS) \
+ $(NULL)
# lua support
if BUILD_LUA
@@ -19,3 +65,9 @@ libmobile_a_SOURCES += script_lua.c
else
libmobile_a_SOURCES += script_nolua.c
endif
+
+# GAPK I/O support
+if BUILD_GAPK
+AM_CPPFLAGS += -DWITH_GAPK_IO=1
+libmobile_a_SOURCES += gapk_io.c
+endif
diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c
index a5feb796..477c4fa5 100644
--- a/src/host/layer23/src/mobile/app_mobile.c
+++ b/src/host/layer23/src/mobile/app_mobile.c
@@ -16,32 +16,35 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
#include <signal.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/gps.h>
+#include <osmocom/bb/common/sap_interface.h>
+#include <osmocom/bb/common/sim.h>
+#include <osmocom/bb/common/l23_app.h>
+
#include <osmocom/bb/mobile/gsm48_rr.h>
#include <osmocom/bb/mobile/gsm480_ss.h>
+#include <osmocom/bb/mobile/gsm48_mm.h>
+#include <osmocom/bb/mobile/gsm48_cc.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
#include <osmocom/bb/mobile/gsm411_sms.h>
+#include <osmocom/bb/mobile/gsm322.h>
#include <osmocom/bb/mobile/vty.h>
#include <osmocom/bb/mobile/app_mobile.h>
#include <osmocom/bb/mobile/mncc.h>
-#include <osmocom/bb/mobile/voice.h>
+#include <osmocom/bb/mobile/tch.h>
#include <osmocom/bb/mobile/primitives.h>
-#include <osmocom/bb/common/sap_interface.h>
-#include <osmocom/vty/ports.h>
-#include <osmocom/vty/logging.h>
+#include <osmocom/vty/vty.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/core/msgb.h>
@@ -51,14 +54,16 @@
#include <l1ctl_proto.h>
+#include "config.h"
+
extern void *l23_ctx;
extern struct llist_head ms_list;
-extern int vty_reading;
-int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg);
+int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg);
+int mncc_recv_external(struct osmocom_ms *ms, int msg_type, void *arg);
int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg);
-int (*mncc_recv_app)(struct osmocom_ms *ms, int, void *);
-static int quit;
+static int _quit;
+extern int quit; /* l23 main */
/* handle ms instance */
int mobile_work(struct osmocom_ms *ms)
@@ -82,12 +87,54 @@ int mobile_work(struct osmocom_ms *ms)
return work;
}
-/* run ms instance, if layer1 is available */
-int mobile_signal_cb(unsigned int subsys, unsigned int signal,
+/* SIM becomes ATTACHED/DETACHED, or answers a request */
+static int mobile_l23_subscr_signal_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
+ struct msgb *nmsg;
+ struct gsm48_mm_event *nmme;
+ struct osmocom_ms *ms;
+ struct osmobb_l23_subscr_sim_auth_resp_sig_data *sim_auth_resp;
+
+ OSMO_ASSERT(subsys == SS_L23_SUBSCR);
+
+ switch (signal) {
+ case S_L23_SUBSCR_SIM_ATTACHED:
+ ms = signal_data;
+ nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmr_downmsg(ms, nmsg);
+ break;
+ case S_L23_SUBSCR_SIM_DETACHED:
+ ms = signal_data;
+ nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ);
+ if (!nmsg)
+ return 0;
+ gsm48_mmr_downmsg(ms, nmsg);
+ break;
+ case S_L23_SUBSCR_SIM_AUTH_RESP:
+ sim_auth_resp = signal_data;
+ ms = sim_auth_resp->ms;
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE);
+ if (!nmsg)
+ return 0;
+ nmme = (struct gsm48_mm_event *) nmsg->data;
+ memcpy(nmme->sres, sim_auth_resp->sres, 4);
+ gsm48_mmevent_msg(ms, nmsg);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
+/* run ms instance, if layer1 is available */
+static int mobile_signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
struct osmocom_ms *ms;
- struct gsm_settings *set;
struct msgb *nmsg;
if (subsys != SS_L1CTL)
@@ -96,7 +143,6 @@ int mobile_signal_cb(unsigned int subsys, unsigned int signal,
switch (signal) {
case S_L1CTL_RESET:
ms = signal_data;
- set = &ms->settings;
/* waiting for reset after shutdown */
if (ms->shutdown == MS_SHUTDOWN_WAIT_RESET) {
@@ -108,21 +154,10 @@ int mobile_signal_cb(unsigned int subsys, unsigned int signal,
if (ms->started)
break;
- /* insert test card, if enabled */
- switch (set->sim_type) {
- case GSM_SIM_TYPE_L1PHY:
- /* trigger sim card reader process */
- gsm_subscr_simcard(ms);
- break;
- case GSM_SIM_TYPE_TEST:
- gsm_subscr_testcard(ms, set->test_rplmn_mcc,
- set->test_rplmn_mnc, set->test_lac,
- set->test_tmsi, set->test_imsi_attached);
- break;
- case GSM_SIM_TYPE_SAP:
- gsm_subscr_sapcard(ms);
- break;
- default:
+ if (ms->settings.sim_type != GSM_SIM_TYPE_NONE) {
+ /* insert sim card */
+ gsm_subscr_insert(ms);
+ } else {
/* no SIM, trigger PLMN selection process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
if (!nmsg)
@@ -167,6 +202,7 @@ int mobile_exit(struct osmocom_ms *ms, int force)
gsm48_cc_exit(ms);
gsm480_ss_exit(ms);
gsm411_sms_exit(ms);
+ gsm44068_gcc_exit(ms);
gsm_sim_exit(ms);
lapdm_channel_exit(&ms->lapdm_channel);
@@ -176,8 +212,8 @@ int mobile_exit(struct osmocom_ms *ms, int force)
} else {
mobile_set_shutdown(ms, MS_SHUTDOWN_COMPL); /* being down */
}
- vty_notify(ms, NULL);
- vty_notify(ms, "Power off!\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Power off!\n");
LOGP(DMOB, LOGL_NOTICE, "Power off! (MS %s)\n", ms->name);
return 0;
@@ -190,26 +226,27 @@ static int mobile_init(struct osmocom_ms *ms)
gsm_settings_arfcn(ms);
- lapdm_channel_init(&ms->lapdm_channel, LAPDM_MODE_MS);
- ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI3].dl.t200_sec =
- T200_DCCH_SHARED;
- ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI3].dl.t200_usec = 0;
- ms->lapdm_channel.lapdm_acch.datalink[DL_SAPI3].dl.t200_sec =
- T200_ACCH;
- ms->lapdm_channel.lapdm_acch.datalink[DL_SAPI3].dl.t200_usec = 0;
+ const int t200_ms_dcch[_NR_DL_SAPI] = {
+ [DL_SAPI0] = 1000,
+ [DL_SAPI3] = 1000 * T200_DCCH_SHARED
+ };
+ const int t200_ms_acch[_NR_DL_SAPI] = {
+ [DL_SAPI0] = 2000,
+ [DL_SAPI3] = 1000 * T200_ACCH
+ };
+
+ lapdm_channel_init3(&ms->lapdm_channel, LAPDM_MODE_MS,
+ t200_ms_dcch, t200_ms_acch,
+ GSM_LCHAN_SDCCH, NULL);
+ lapdm_channel_set_flags(&ms->lapdm_channel, LAPDM_ENT_F_DROP_2ND_REJ);
lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms);
- /* init SAP client before SIM card starts up */
- sap_init(ms);
-
- /* SAP response call-back */
- ms->sap_entity.sap_rsp_cb = &gsm_subscr_sap_rsp_cb;
-
gsm_sim_init(ms);
gsm48_cc_init(ms);
gsm480_ss_init(ms);
gsm411_sms_init(ms);
- gsm_voice_init(ms);
+ gsm44068_gcc_init(ms);
+ tch_init(ms);
gsm_subscr_init(ms);
gsm48_rr_init(ms);
gsm48_mm_init(ms);
@@ -236,6 +273,24 @@ static int mobile_init(struct osmocom_ms *ms)
"default IMEI.\n***\n");
}
+ switch (ms->settings.mncc_handler) {
+ case MNCC_HANDLER_INTERNAL:
+ LOGP(DMOB, LOGL_INFO, "Using the built-in MNCC-handler for MS '%s'\n", ms->name);
+ ms->mncc_entity.mncc_recv = &mncc_recv_internal;
+ break;
+ case MNCC_HANDLER_EXTERNAL:
+ LOGP(DMOB, LOGL_INFO, "Using external MNCC-handler (socket '%s') for MS '%s'\n",
+ ms->settings.mncc_socket_path, ms->name);
+ ms->mncc_entity.mncc_recv = &mncc_recv_external;
+ ms->mncc_entity.sock_state = mncc_sock_init(ms, ms->settings.mncc_socket_path);
+ break;
+ case MNCC_HANDLER_DUMMY:
+ default:
+ LOGP(DMOB, LOGL_INFO, "Using dummy MNCC-handler (no call support) "
+ "for MS '%s'\n", ms->name);
+ ms->mncc_entity.mncc_recv = &mncc_recv_dummy;
+ }
+
l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
LOGP(DMOB, LOGL_NOTICE, "Mobile '%s' initialized, please start phone now!\n", ms->name);
return 0;
@@ -255,7 +310,7 @@ int mobile_start(struct osmocom_ms *ms, char **other_name)
if (!strcmp(ms->settings.layer2_socket_path,
tmp->settings.layer2_socket_path)) {
LOGP(DMOB, LOGL_ERROR, "Cannot start MS '%s', because MS '%s' "
- "use the same layer2-socket.\nPlease shutdown "
+ "is using the same layer2-socket.\nPlease shutdown "
"MS '%s' first.\n", ms->name, tmp->name, tmp->name);
*other_name = tmp->name;
return -1;
@@ -263,11 +318,19 @@ int mobile_start(struct osmocom_ms *ms, char **other_name)
if (!strcmp(ms->settings.sap_socket_path,
tmp->settings.sap_socket_path)) {
LOGP(DMOB, LOGL_ERROR, "Cannot start MS '%s', because MS '%s' "
- "use the same sap-socket.\nPlease shutdown "
+ "is using the same sap-socket.\nPlease shutdown "
"MS '%s' first.\n", ms->name, tmp->name, tmp->name);
*other_name = tmp->name;
return -2;
}
+ if (!strcmp(ms->settings.mncc_socket_path,
+ tmp->settings.mncc_socket_path)) {
+ LOGP(DMOB, LOGL_ERROR, "Cannot start MS '%s', because MS '%s' "
+ "is using the same mncc-socket.\nPlease shutdown "
+ "MS '%s' first.\n", ms->name, tmp->name, tmp->name);
+ *other_name = tmp->name;
+ return -3;
+ }
}
rc = mobile_init(ms);
@@ -290,40 +353,14 @@ int mobile_stop(struct osmocom_ms *ms, int force)
struct osmocom_ms *mobile_new(char *name)
{
static struct osmocom_ms *ms;
- char *mncc_name;
- ms = talloc_zero(l23_ctx, struct osmocom_ms);
+ ms = osmocom_ms_alloc(l23_ctx, name);
if (!ms) {
LOGP(DMOB, LOGL_ERROR, "Failed to allocate MS: %s\n", name);
return NULL;
}
- talloc_set_name(ms, "ms_%s", name);
- ms->name = talloc_strdup(ms, name);
- ms->l2_wq.bfd.fd = -1;
- ms->sap_wq.bfd.fd = -1;
-
- /* Register a new MS */
- llist_add_tail(&ms->entity, &ms_list);
-
- gsm_support_init(ms);
- gsm_settings_init(ms);
-
mobile_set_shutdown(ms, MS_SHUTDOWN_COMPL);
-
- if (mncc_recv_app) {
- mncc_name = talloc_asprintf(ms, "/tmp/ms_mncc_%s", ms->name);
-
- ms->mncc_entity.mncc_recv = mncc_recv_app;
- ms->mncc_entity.sock_state = mncc_sock_init(ms, mncc_name);
-
- talloc_free(mncc_name);
- } else if (ms->settings.ch_cap == GSM_CAP_SDCCH)
- ms->mncc_entity.mncc_recv = mncc_recv_dummy;
- else
- ms->mncc_entity.mncc_recv = mncc_recv_mobile;
-
-
return ms;
}
@@ -334,7 +371,7 @@ int mobile_delete(struct osmocom_ms *ms, int force)
ms->deleting = true;
- if (mncc_recv_app) {
+ if (ms->settings.mncc_handler == MNCC_HANDLER_EXTERNAL) {
mncc_sock_exit(ms->mncc_entity.sock_state);
ms->mncc_entity.sock_state = NULL;
}
@@ -349,8 +386,8 @@ int mobile_delete(struct osmocom_ms *ms, int force)
}
/* handle global shutdown */
-int global_signal_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
+static int global_signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
{
struct osmocom_ms *ms, *ms2;
@@ -361,20 +398,20 @@ int global_signal_cb(unsigned int subsys, unsigned int signal,
case S_GLOBAL_SHUTDOWN:
/* force to exit, if signalled */
if (signal_data && *((uint8_t *)signal_data))
- quit = 1;
+ _quit = 1;
llist_for_each_entry_safe(ms, ms2, &ms_list, entity)
- mobile_delete(ms, quit);
+ mobile_delete(ms, _quit);
/* quit, after all MS processes are gone */
- quit = 1;
+ _quit = 1;
break;
}
return 0;
}
/* global work handler */
-int l23_app_work(int *_quit)
+static int _mobile_app_work(void)
{
struct osmocom_ms *ms, *ms2;
int work = 0;
@@ -387,14 +424,9 @@ int l23_app_work(int *_quit)
layer2_close(ms);
ms->l2_wq.bfd.fd = -1;
}
-
- if (ms->sap_wq.bfd.fd > -1) {
- sap_close(ms);
- ms->sap_wq.bfd.fd = -1;
- }
-
if (ms->deleting) {
gsm_settings_exit(ms);
+ script_lua_close(ms);
llist_del(&ms->entity);
talloc_free(ms);
work = 1;
@@ -403,70 +435,27 @@ int l23_app_work(int *_quit)
}
/* return, if a shutdown was scheduled (quit = 1) */
- *_quit = quit;
+ quit = _quit;
return work;
}
/* global exit */
-int l23_app_exit(void)
+static int _mobile_app_exit(void)
{
+ osmo_signal_unregister_handler(SS_L23_SUBSCR, &mobile_l23_subscr_signal_cb, NULL);
osmo_signal_unregister_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
osmo_signal_unregister_handler(SS_L1CTL, &mobile_signal_cb, NULL);
osmo_signal_unregister_handler(SS_GLOBAL, &global_signal_cb, NULL);
osmo_gps_close();
- telnet_exit();
-
return 0;
}
-static struct vty_app_info vty_info = {
- .name = "OsmocomBB",
- .version = PACKAGE_VERSION,
- .go_parent_cb = ms_vty_go_parent,
-};
-/* global init */
-int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
- const char *config_file)
+static int _mobile_app_start(void)
{
- struct telnet_connection dummy_conn;
- int rc = 0;
-
- mncc_recv_app = mncc_recv;
-
- osmo_gps_init();
-
- vty_info.tall_ctx = l23_ctx;
- vty_init(&vty_info);
- logging_vty_add_cmds();
- ms_vty_init();
- dummy_conn.priv = NULL;
- vty_reading = 1;
- if (config_file != NULL) {
- rc = vty_read_config_file(config_file, &dummy_conn);
- if (rc < 0) {
- LOGP(DMOB, LOGL_FATAL, "Failed to parse the configuration "
- "file '%s'\n", config_file);
- LOGP(DMOB, LOGL_FATAL, "Please make sure the file "
- "'%s' exists, or use an example from "
- "'doc/examples/mobile/'\n", config_file);
- return rc;
- }
- LOGP(DMOB, LOGL_INFO, "Using configuration from '%s'\n", config_file);
- }
- vty_reading = 0;
- rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB);
- if (rc < 0) {
- LOGP(DMOB, LOGL_FATAL, "Cannot init VTY on %s port %u: %s\n",
- vty_get_bind_addr(), OSMO_VTY_PORT_BB, strerror(errno));
- return rc;
- }
-
- osmo_signal_register_handler(SS_GLOBAL, &global_signal_cb, NULL);
- osmo_signal_register_handler(SS_L1CTL, &mobile_signal_cb, NULL);
- osmo_signal_register_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
+ int rc;
if (llist_empty(&ms_list)) {
struct osmocom_ms *ms;
@@ -481,11 +470,45 @@ int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
return rc;
}
- quit = 0;
+ _quit = 0;
+
+ return 0;
+}
+
+/* global init */
+int l23_app_init(void)
+{
+ l23_app_start = _mobile_app_start;
+ l23_app_work = _mobile_app_work;
+ l23_app_exit = _mobile_app_exit;
+ osmo_gps_init();
+
+ osmo_signal_register_handler(SS_GLOBAL, &global_signal_cb, NULL);
+ osmo_signal_register_handler(SS_L1CTL, &mobile_signal_cb, NULL);
+ osmo_signal_register_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
+ osmo_signal_register_handler(SS_L23_SUBSCR, &mobile_l23_subscr_signal_cb, NULL);
return 0;
}
+static int _mobile_vty_init(void)
+{
+ return ms_vty_init();
+}
+
+static struct vty_app_info _mobile_vty_info = {
+ .name = "OsmocomBB(mobile)",
+ .version = PACKAGE_VERSION,
+};
+
+const struct l23_app_info l23_app_info = {
+ .copyright = "Copyright (C) 2010-2015 Andreas Eversberg, Sylvain Munaut, Holger Freyther, Harald Welte\n",
+ .contribution = "Contributions by Alex Badea, Pablo Neira, Steve Markgraf and others\n",
+ .opt_supported = L23_OPT_TAP | L23_OPT_VTY | L23_OPT_DBG,
+ .vty_info = &_mobile_vty_info,
+ .vty_init = _mobile_vty_init,
+};
+
void mobile_set_started(struct osmocom_ms *ms, bool state)
{
ms->started = state;
diff --git a/src/host/layer23/src/mobile/gapk_io.c b/src/host/layer23/src/mobile/gapk_io.c
new file mode 100644
index 00000000..cc756b06
--- /dev/null
+++ b/src/host/layer23/src/mobile/gapk_io.c
@@ -0,0 +1,547 @@
+/*
+ * GAPK (GSM Audio Pocket Knife) based audio I/O
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/common.h>
+
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/logging.h>
+
+#include <osmocom/bb/mobile/tch.h>
+#include <osmocom/bb/mobile/gapk_io.h>
+
+/* The RAW PCM format is common for both audio source and sink */
+static const struct osmo_gapk_format_desc *rawpcm_fmt;
+
+static int pq_queue_tch_fb_recv(void *_state, uint8_t *out,
+ const uint8_t *in, unsigned int in_len)
+{
+ struct gapk_io_state *state = (struct gapk_io_state *)_state;
+ struct msgb *tch_msg;
+ size_t frame_len;
+
+ /* Obtain one TCH frame from the DL buffer */
+ tch_msg = msgb_dequeue_count(&state->tch_dl_fb,
+ &state->tch_dl_fb_len);
+ if (tch_msg == NULL)
+ return -EIO;
+
+ /* Calculate received frame length */
+ frame_len = msgb_l3len(tch_msg);
+ if (frame_len == 0) {
+ msgb_free(tch_msg);
+ return -EIO;
+ }
+
+ /* Copy the frame bytes from message */
+ memcpy(out, tch_msg->l3h, frame_len);
+
+ /* Release memory */
+ msgb_free(tch_msg);
+
+ return frame_len;
+}
+
+static int pq_queue_tch_fb_send(void *_state, uint8_t *out,
+ const uint8_t *in, unsigned int in_len)
+{
+ struct gapk_io_state *state = (struct gapk_io_state *)_state;
+ struct msgb *tch_msg;
+
+ if (state->tch_ul_fb_len >= GAPK_ULDL_QUEUE_LIMIT) {
+ LOGP(DGAPK, LOGL_ERROR, "UL TCH frame buffer overflow, dropping msg\n");
+ return -EOVERFLOW;
+ }
+
+ /* Allocate a new message for the lower layers */
+ tch_msg = msgb_alloc_headroom(in_len + 64, 64, "TCH frame");
+ if (tch_msg == NULL)
+ return -ENOMEM;
+
+ /* Copy the frame bytes to a new message */
+ tch_msg->l2h = msgb_put(tch_msg, in_len);
+ memcpy(tch_msg->l2h, in, in_len);
+
+ /* Put encoded TCH frame to the UL buffer */
+ msgb_enqueue_count(&state->tch_ul_fb, tch_msg,
+ &state->tch_ul_fb_len);
+
+ return 0;
+}
+
+/**
+ * A custom TCH frame buffer block, which actually
+ * handles incoming frames from DL buffer and puts
+ * outgoing frames to UL buffer...
+ */
+static int pq_queue_tch_fb(struct osmo_gapk_pq *pq,
+ struct gapk_io_state *state,
+ bool is_src)
+{
+ struct osmo_gapk_pq_item *item;
+ unsigned int frame_len;
+
+ LOGP(DGAPK, LOGL_DEBUG, "PQ '%s': Adding TCH frame buffer %s\n",
+ pq->name, is_src ? "input" : "output");
+
+ /* Allocate and add a new queue item */
+ item = osmo_gapk_pq_add_item(pq);
+ if (item == NULL)
+ return -ENOMEM;
+
+ /* General item type and description */
+ item->type = is_src ? OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK;
+ item->cat_name = is_src ? "source" : "sink";
+ item->sub_name = "tch_fb";
+
+ /* I/O length */
+ frame_len = state->phy_fmt_desc->frame_len;
+ item->len_in = is_src ? 0 : frame_len;
+ item->len_out = is_src ? frame_len : 0;
+
+ /* Handler and it's state */
+ item->proc = is_src ? &pq_queue_tch_fb_recv : &pq_queue_tch_fb_send;
+ item->state = state;
+
+ return 0;
+}
+
+/**
+ * Auxiliary wrapper around format conversion block.
+ * Is used to perform either a conversion from the format,
+ * produced by encoder, to canonical, or a conversion
+ * from canonical format to the format expected by decoder.
+ */
+static int pq_queue_codec_fmt_conv(struct osmo_gapk_pq *pq,
+ const struct osmo_gapk_codec_desc *codec,
+ bool is_src)
+{
+ const struct osmo_gapk_format_desc *codec_fmt_desc;
+
+ /* Get format description */
+ codec_fmt_desc = osmo_gapk_fmt_get_from_type(is_src ?
+ codec->codec_enc_format_type : codec->codec_dec_format_type);
+ if (codec_fmt_desc == NULL)
+ return -ENOTSUP;
+
+ /* Put format conversion block */
+ return osmo_gapk_pq_queue_fmt_convert(pq, codec_fmt_desc, !is_src);
+}
+
+/**
+ * Prepares the following queue (source is mic):
+ *
+ * source/alsa -> proc/codec -> proc/format ->
+ * -> proc/format -> sink/tch_fb
+ *
+ * The two format conversion blocks are aimed to
+ * convert an encoder specific format
+ * to a PHY specific format.
+ */
+static int prepare_audio_source(struct gapk_io_state *state,
+ const char *alsa_input_dev)
+{
+ struct osmo_gapk_pq *pq;
+ char *pq_desc;
+ int rc;
+
+ LOGP(DGAPK, LOGL_DEBUG, "Prepare audio input (capture) chain\n");
+
+ /* Allocate a processing queue */
+ pq = osmo_gapk_pq_create("pq_audio_source");
+ if (pq == NULL)
+ return -ENOMEM;
+
+ /* ALSA audio source */
+ rc = osmo_gapk_pq_queue_alsa_input(pq, alsa_input_dev, rawpcm_fmt->frame_len);
+ if (rc)
+ goto error;
+
+ /* Frame encoder */
+ rc = osmo_gapk_pq_queue_codec(pq, state->codec_desc, 1);
+ if (rc)
+ goto error;
+
+ /* Encoder specific format -> canonical */
+ rc = pq_queue_codec_fmt_conv(pq, state->codec_desc, true);
+ if (rc)
+ goto error;
+
+ /* Canonical -> PHY specific format */
+ rc = osmo_gapk_pq_queue_fmt_convert(pq, state->phy_fmt_desc, 1);
+ if (rc)
+ goto error;
+
+ /* TCH frame buffer sink */
+ rc = pq_queue_tch_fb(pq, state, false);
+ if (rc)
+ goto error;
+
+ /* Check composed queue in strict mode */
+ rc = osmo_gapk_pq_check(pq, 1);
+ if (rc)
+ goto error;
+
+ /* Prepare queue (allocate buffers, etc.) */
+ rc = osmo_gapk_pq_prepare(pq);
+ if (rc)
+ goto error;
+
+ /* Save pointer within MS GAPK state */
+ state->pq_source = pq;
+
+ /* Describe prepared chain */
+ pq_desc = osmo_gapk_pq_describe(pq);
+ LOGP(DGAPK, LOGL_DEBUG, "PQ '%s': chain '%s' prepared\n", pq->name, pq_desc);
+ talloc_free(pq_desc);
+
+ return 0;
+
+error:
+ talloc_free(pq);
+ return rc;
+}
+
+/**
+ * Prepares the following queue (sink is speaker):
+ *
+ * src/tch_fb -> proc/format -> [proc/ecu] ->
+ * proc/format -> proc/codec -> sink/alsa
+ *
+ * The two format conversion blocks (proc/format)
+ * are aimed to convert a PHY specific format
+ * to an encoder specific format.
+ *
+ * A ECU (Error Concealment Unit) block is optionally
+ * added if implemented for a given codec.
+ */
+static int prepare_audio_sink(struct gapk_io_state *state,
+ const char *alsa_output_dev)
+{
+ struct osmo_gapk_pq *pq;
+ char *pq_desc;
+ int rc;
+
+ LOGP(DGAPK, LOGL_DEBUG, "Prepare audio output (playback) chain\n");
+
+ /* Allocate a processing queue */
+ pq = osmo_gapk_pq_create("pq_audio_sink");
+ if (pq == NULL)
+ return -ENOMEM;
+
+ /* TCH frame buffer source */
+ rc = pq_queue_tch_fb(pq, state, true);
+ if (rc)
+ goto error;
+
+ /* PHY specific format -> canonical */
+ rc = osmo_gapk_pq_queue_fmt_convert(pq, state->phy_fmt_desc, 0);
+ if (rc)
+ goto error;
+
+ /* Optional ECU (Error Concealment Unit) */
+ osmo_gapk_pq_queue_ecu(pq, state->codec_desc);
+
+ /* Canonical -> decoder specific format */
+ rc = pq_queue_codec_fmt_conv(pq, state->codec_desc, false);
+ if (rc)
+ goto error;
+
+ /* Frame decoder */
+ rc = osmo_gapk_pq_queue_codec(pq, state->codec_desc, 0);
+ if (rc)
+ goto error;
+
+ /* ALSA audio sink */
+ rc = osmo_gapk_pq_queue_alsa_output(pq, alsa_output_dev, rawpcm_fmt->frame_len);
+ if (rc)
+ goto error;
+
+ /* Check composed queue in strict mode */
+ rc = osmo_gapk_pq_check(pq, 1);
+ if (rc)
+ goto error;
+
+ /* Prepare queue (allocate buffers, etc.) */
+ rc = osmo_gapk_pq_prepare(pq);
+ if (rc)
+ goto error;
+
+ /* Save pointer within MS GAPK state */
+ state->pq_sink = pq;
+
+ /* Describe prepared chain */
+ pq_desc = osmo_gapk_pq_describe(pq);
+ LOGP(DGAPK, LOGL_DEBUG, "PQ '%s': chain '%s' prepared\n", pq->name, pq_desc);
+ talloc_free(pq_desc);
+
+ return 0;
+
+error:
+ talloc_free(pq);
+ return rc;
+}
+
+/**
+ * Cleans up both TCH frame I/O buffers, destroys both
+ * processing queues (chains), and deallocates the memory.
+ * Should be called when a voice call is finished...
+ */
+void gapk_io_state_free(struct gapk_io_state *state)
+{
+ struct msgb *msg;
+
+ if (state == NULL)
+ return;
+
+ /* Flush TCH frame I/O buffers */
+ while ((msg = msgb_dequeue(&state->tch_dl_fb)))
+ msgb_free(msg);
+ while ((msg = msgb_dequeue(&state->tch_ul_fb)))
+ msgb_free(msg);
+
+ /* Destroy both audio I/O chains */
+ if (state->pq_source != NULL)
+ osmo_gapk_pq_destroy(state->pq_source);
+ if (state->pq_sink != NULL)
+ osmo_gapk_pq_destroy(state->pq_sink);
+
+ talloc_free(state);
+}
+
+/**
+ * Picks the corresponding PHY's frame format for a given codec.
+ * To be used with PHYs that produce audio frames in RTP format,
+ * such as trxcon (GSM 05.03 libosmocoding API).
+ */
+static enum osmo_gapk_format_type phy_fmt_pick_rtp(enum osmo_gapk_codec_type codec)
+{
+ switch (codec) {
+ case CODEC_HR:
+ return FMT_RTP_HR_IETF;
+ case CODEC_FR:
+ return FMT_GSM;
+ case CODEC_EFR:
+ return FMT_RTP_EFR;
+ case CODEC_AMR:
+ return FMT_RTP_AMR;
+ default:
+ return FMT_INVALID;
+ }
+}
+
+/**
+ * Picks the corresponding PHY's frame format for a given codec.
+ * To be used with PHYs that produce audio in TI Calypso format.
+ */
+static enum osmo_gapk_format_type phy_fmt_pick_ti(enum osmo_gapk_codec_type codec)
+{
+ switch (codec) {
+ case CODEC_HR:
+ return FMT_TI_HR;
+ case CODEC_FR:
+ return FMT_TI_FR;
+ case CODEC_EFR:
+ return FMT_TI_EFR;
+ case CODEC_AMR: /* not supported */
+ default:
+ return FMT_INVALID;
+ }
+}
+
+/**
+ * Allocates both TCH frame I/O buffers
+ * and prepares both processing queues (chains).
+ * Should be called when a voice call is initiated...
+ */
+struct gapk_io_state *
+gapk_io_state_alloc(struct osmocom_ms *ms,
+ enum osmo_gapk_codec_type codec)
+{
+ const struct osmo_gapk_format_desc *phy_fmt_desc;
+ const struct osmo_gapk_codec_desc *codec_desc;
+ const struct gsm_settings *set = &ms->settings;
+ enum osmo_gapk_format_type phy_fmt;
+ struct gapk_io_state *state;
+ int rc = 0;
+
+ LOGP(DGAPK, LOGL_NOTICE, "Initialize GAPK I/O\n");
+
+ /* Make sure that the chosen codec has description */
+ codec_desc = osmo_gapk_codec_get_from_type(codec);
+ if (codec_desc == NULL) {
+ LOGP(DGAPK, LOGL_ERROR, "Invalid codec type 0x%02x\n", codec);
+ return NULL;
+ }
+
+ /* Make sure that the chosen codec is supported */
+ if (codec_desc->codec_encode == NULL || codec_desc->codec_decode == NULL) {
+ LOGP(DGAPK, LOGL_ERROR,
+ "Codec '%s' is not supported by GAPK\n", codec_desc->name);
+ return NULL;
+ }
+
+ switch (set->tch_voice.io_format) {
+ case TCH_VOICE_IOF_RTP:
+ phy_fmt = phy_fmt_pick_rtp(codec);
+ break;
+ case TCH_VOICE_IOF_TI:
+ phy_fmt = phy_fmt_pick_ti(codec);
+ break;
+ default:
+ LOGP(DGAPK, LOGL_ERROR, "Unhandled I/O format %s\n",
+ tch_voice_io_format_name(set->tch_voice.io_format));
+ return NULL;
+ }
+
+ phy_fmt_desc = osmo_gapk_fmt_get_from_type(phy_fmt);
+ if (phy_fmt_desc == NULL) {
+ LOGP(DGAPK, LOGL_ERROR, "Failed to pick the PHY specific "
+ "frame format for codec '%s'\n", codec_desc->name);
+ return NULL;
+ }
+
+ state = talloc_zero(ms, struct gapk_io_state);
+ if (state == NULL) {
+ LOGP(DGAPK, LOGL_ERROR, "Failed to allocate memory\n");
+ return NULL;
+ }
+
+ /* Init TCH frame I/O buffers */
+ INIT_LLIST_HEAD(&state->tch_dl_fb);
+ INIT_LLIST_HEAD(&state->tch_ul_fb);
+
+ /* Store the codec / format description */
+ state->codec_desc = codec_desc;
+ state->phy_fmt_desc = phy_fmt_desc;
+
+ /* Use gapk_io_state as talloc context for both chains */
+ osmo_gapk_set_talloc_ctx(state);
+
+ /* Prepare both source and sink chains */
+ rc |= prepare_audio_source(state, set->tch_voice.alsa_input_dev);
+ rc |= prepare_audio_sink(state, set->tch_voice.alsa_output_dev);
+
+ /* Fall back to ms instance */
+ osmo_gapk_set_talloc_ctx(ms);
+
+ /* If at lease one chain constructor failed */
+ if (rc) {
+ /* Destroy both audio I/O chains */
+ if (state->pq_source)
+ osmo_gapk_pq_destroy(state->pq_source);
+ if (state->pq_sink)
+ osmo_gapk_pq_destroy(state->pq_sink);
+
+ /* Release the memory and return */
+ talloc_free(state);
+
+ LOGP(DGAPK, LOGL_ERROR, "Failed to initialize GAPK I/O\n");
+ return NULL;
+ }
+
+ LOGP(DGAPK, LOGL_NOTICE,
+ "GAPK I/O initialized for MS '%s', codec '%s'\n",
+ ms->name, codec_desc->name);
+
+ return state;
+}
+
+/* gapk_io_init_ms() wrapper, selecting a codec based on channel mode and rate */
+struct gapk_io_state *
+gapk_io_state_alloc_mode_rate(struct osmocom_ms *ms,
+ enum gsm48_chan_mode ch_mode,
+ bool full_rate)
+{
+ enum osmo_gapk_codec_type codec;
+
+ switch (ch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR or HR */
+ codec = full_rate ? CODEC_FR : CODEC_HR;
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ codec = CODEC_EFR;
+ break;
+ case GSM48_CMODE_SPEECH_AMR:
+ codec = CODEC_AMR;
+ break;
+ default:
+ LOGP(DGAPK, LOGL_ERROR, "Unhandled channel mode 0x%02x (%s)\n",
+ ch_mode, get_value_string(gsm48_chan_mode_names, ch_mode));
+ return NULL;
+ }
+
+ return gapk_io_state_alloc(ms, codec);
+}
+
+/* Enqueue a Downlink TCH frame */
+void gapk_io_enqueue_dl(struct gapk_io_state *state, struct msgb *msg)
+{
+ if (state->tch_dl_fb_len >= GAPK_ULDL_QUEUE_LIMIT) {
+ LOGP(DGAPK, LOGL_ERROR, "DL TCH frame buffer overflow, dropping msg\n");
+ msgb_free(msg);
+ return;
+ }
+
+ msgb_enqueue_count(&state->tch_dl_fb, msg,
+ &state->tch_dl_fb_len);
+
+ /* Decode and play a received DL TCH frame */
+ osmo_gapk_pq_execute(state->pq_sink);
+}
+
+/* Dequeue an Uplink TCH frame */
+void gapk_io_dequeue_ul(struct osmocom_ms *ms, struct gapk_io_state *state)
+{
+ struct msgb *msg;
+
+ /* Record and encode an UL TCH frame */
+ osmo_gapk_pq_execute(state->pq_source);
+
+ /* Obtain one TCH frame from the UL buffer */
+ msg = msgb_dequeue_count(&state->tch_ul_fb, &state->tch_ul_fb_len);
+ if (msg != NULL)
+ tch_send_msg(ms, msg);
+}
+
+/**
+ * Performs basic initialization of GAPK library,
+ * setting the talloc root context and a logging category.
+ */
+static __attribute__((constructor)) void gapk_io_init(void)
+{
+ /* Init logging subsystem */
+ osmo_gapk_log_init(DGAPK);
+
+ /* Make RAWPCM format info easy to access */
+ rawpcm_fmt = osmo_gapk_fmt_get_from_type(FMT_RAWPCM_S16LE);
+}
diff --git a/src/host/layer23/src/mobile/gsm322.c b/src/host/layer23/src/mobile/gsm322.c
index cc4f0cd0..3d0cc77d 100644
--- a/src/host/layer23/src/mobile/gsm322.c
+++ b/src/host/layer23/src/mobile/gsm322.c
@@ -13,10 +13,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 <stdint.h>
@@ -36,18 +32,21 @@
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/l1ctl.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/networks.h>
+#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/common/settings.h>
#include <osmocom/bb/mobile/vty.h>
#include <osmocom/bb/mobile/app_mobile.h>
-#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/mobile/gsm322.h>
+#include <osmocom/bb/mobile/gsm48_mm.h>
#include <l1ctl_proto.h>
const char *ba_version = "osmocom BA V1\n";
static void gsm322_cs_timeout(void *arg);
-static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc,
- uint16_t mnc, int any);
+static int gsm322_cs_select(struct osmocom_ms *ms, int index, const struct osmo_plmn_id *plmn, int any);
static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
static void gsm322_any_timeout(void *arg);
static int gsm322_nb_scan(struct osmocom_ms *ms);
@@ -513,7 +512,7 @@ static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
cs->si->si5 = 0; /* unset SI5* */
cs->si = NULL;
memset(&cs->sel_si, 0, sizeof(cs->sel_si));
- cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
+ memset(&cs->sel_cgi, 0, sizeof(cs->sel_cgi));
}
/* print to DCS logging */
@@ -535,17 +534,15 @@ static void print_dcs(void *priv, const char *fmt, ...)
}
/* del forbidden LA */
-int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
- uint16_t mnc, uint16_t lac)
+int gsm322_del_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai)
{
struct gsm322_plmn *plmn = &ms->plmn;
struct gsm322_la_list *la;
llist_for_each_entry(la, &plmn->forbidden_la, entry) {
- if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
- LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
- "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
- gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
+ if (osmo_lai_cmp(&la->lai, lai) == 0) {
+ LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden LAs (LAI=%s)\n",
+ osmo_lai_name(lai));
llist_del(&la->entry);
talloc_free(la);
return 0;
@@ -556,21 +553,16 @@ int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
}
/* add forbidden LA */
-int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
- uint16_t mnc, uint16_t lac, uint8_t cause)
+int gsm322_add_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai, uint8_t cause)
{
struct gsm322_plmn *plmn = &ms->plmn;
struct gsm322_la_list *la;
- LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
- "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
- gsm_print_mnc(mnc), lac);
+ LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs (LAI=%s)\n", osmo_lai_name(lai));
la = talloc_zero(ms, struct gsm322_la_list);
if (!la)
return -ENOMEM;
- la->mcc = mcc;
- la->mnc = mnc;
- la->lac = lac;
+ la->lai = *lai;
la->cause = cause;
llist_add_tail(&la->entry, &plmn->forbidden_la);
@@ -578,14 +570,13 @@ int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
}
/* search forbidden LA */
-int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
- uint16_t lac)
+int gsm322_is_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai)
{
struct gsm322_plmn *plmn = &ms->plmn;
struct gsm322_la_list *la;
llist_for_each_entry(la, &plmn->forbidden_la, entry) {
- if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
+ if (osmo_lai_cmp(&la->lai, lai) == 0)
return 1;
}
@@ -593,15 +584,13 @@ int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
}
/* search for PLMN in all BA lists */
-static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
- uint16_t mcc, uint16_t mnc)
+static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn)
{
struct gsm322_ba_list *ba, *ba_found = NULL;
/* search for BA list */
llist_for_each_entry(ba, &cs->ba_list, entry) {
- if (ba->mcc == mcc
- && ba->mnc == mnc) {
+ if (osmo_plmn_cmp(&ba->plmn, plmn) == 0) {
ba_found = ba;
break;
}
@@ -611,16 +600,14 @@ static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
}
/* search available PLMN */
-int gsm322_is_plmn_avail_and_allow(struct gsm322_cellsel *cs, uint16_t mcc,
- uint16_t mnc)
+int gsm322_is_plmn_avail_and_allow(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn)
{
int i;
for (i = 0; i <= 1023+299; i++) {
if ((cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
&& cs->list[i].sysinfo
- && cs->list[i].sysinfo->mcc == mcc
- && cs->list[i].sysinfo->mnc == mnc)
+ && (osmo_plmn_cmp(&cs->list[i].sysinfo->lai.plmn, plmn) == 0))
return 1;
}
@@ -635,9 +622,12 @@ int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
for (i = 0; i <= 1023+299; i++) {
if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
&& cs->list[i].sysinfo
- && gsm_match_mnc(cs->list[i].sysinfo->mcc,
- cs->list[i].sysinfo->mnc, imsi))
+ && gsm_match_mnc(cs->list[i].sysinfo->lai.plmn.mcc,
+ cs->list[i].sysinfo->lai.plmn.mnc,
+ cs->list[i].sysinfo->lai.plmn.mnc_3_digits,
+ imsi))
return 1;
+ /* TODO: take into account mnc_3_digits, probably use osmo_mnc_cmp()*/
}
return 0;
@@ -899,8 +889,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
/* search if network has multiple cells */
found = NULL;
llist_for_each_entry(temp, &temp_list, entry) {
- if (temp->mcc == cs->list[i].sysinfo->mcc
- && temp->mnc == cs->list[i].sysinfo->mnc) {
+ if (osmo_plmn_cmp(&temp->plmn, &cs->list[i].sysinfo->lai.plmn) == 0) {
found = temp;
break;
}
@@ -913,8 +902,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
temp = talloc_zero(ms, struct gsm322_plmn_list);
if (!temp)
return -ENOMEM;
- temp->mcc = cs->list[i].sysinfo->mcc;
- temp->mnc = cs->list[i].sysinfo->mnc;
+ memcpy(&temp->plmn, &cs->list[i].sysinfo->lai.plmn, sizeof(temp->plmn));
temp->rxlev = cs->list[i].rxlev;
llist_add_tail(&temp->entry, &temp_list);
}
@@ -924,7 +912,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
if (subscr->sim_valid) {
found = NULL;
llist_for_each_entry(temp, &temp_list, entry) {
- if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
+ if (gsm_match_mnc(temp->plmn.mcc, temp->plmn.mnc, temp->plmn.mnc_3_digits, subscr->imsi)) {
found = temp;
break;
}
@@ -940,8 +928,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
found = NULL;
llist_for_each_entry(temp, &temp_list, entry) {
- if (temp->mcc == sim_entry->mcc
- && temp->mnc == sim_entry->mnc) {
+ if (osmo_plmn_cmp(&temp->plmn, &sim_entry->plmn) == 0) {
found = temp;
break;
}
@@ -995,16 +982,15 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
i = 0;
llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
- if (temp->mcc == na_entry->mcc
- && temp->mnc == na_entry->mnc) {
+ if (osmo_plmn_cmp(&temp->plmn, &na_entry->plmn) == 0) {
temp->cause = na_entry->cause;
break;
}
}
LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
- "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n",
- i, gsm_print_mcc(temp->mcc),
- gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
+ "(%02d: mcc-mnc %s allowed %s rx-lev %s)\n",
+ i, osmo_plmn_name(&temp->plmn),
+ (temp->cause) ? "no ":"yes",
gsm_print_rxlev(temp->rxlev));
i++;
}
@@ -1027,9 +1013,9 @@ static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
new_a_state(plmn, GSM322_A2_ON_PLMN);
/* start timer, if on VPLMN of home country OR special case */
- if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
+ if (!gsm_match_mnc(plmn->plmn.mcc, plmn->plmn.mnc, plmn->plmn.mnc_3_digits, subscr->imsi)
&& (subscr->always_search_hplmn
- || gsm_match_mcc(plmn->mcc, subscr->imsi))
+ || gsm_match_mcc(plmn->plmn.mcc, subscr->imsi))
&& subscr->sim_valid && subscr->t6m_hplmn)
start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
else
@@ -1070,7 +1056,7 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
int found;
/* any allowable PLMN available? */
- found = gsm322_cs_select(ms, -1, 0, 0, 0);
+ found = gsm322_cs_select(ms, -1, NULL, 0);
/* if no PLMN in list:
* this means that we are at a point where we camp on any cell or
@@ -1079,32 +1065,28 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
if (subscr->plmn_valid) {
LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. "
"Do limited search with RPLMN.\n");
- plmn->mcc = subscr->plmn_mcc;
- plmn->mnc = subscr->plmn_mnc;
+ memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id));
} else
if (subscr->sim_valid) {
LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. "
"Do limited search with HPLMN.\n");
- plmn->mcc = subscr->mcc;
- plmn->mnc = subscr->mnc;
+ memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id));
} else {
LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. "
"Do limited search with no PLMN.\n");
- plmn->mcc = 0;
- plmn->mnc = 0;
+ memset(&plmn->plmn, 0, sizeof(struct osmo_plmn_id));
}
return gsm322_a_go_wait_for_plmns(ms, msg);
}
/* select first PLMN in list */
- plmn->mcc = cs->list[found].sysinfo->mcc;
- plmn->mnc = cs->list[found].sysinfo->mnc;
+ memcpy(&plmn->plmn, &cs->list[found].sysinfo->lai.plmn, sizeof(struct osmo_plmn_id));
LOGP(DPLMN, LOGL_INFO, "PLMN available after searching PLMN list "
- "(mcc=%s mnc=%s %s, %s)\n",
- gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
- gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+ "(mcc=-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc), gsm_get_mnc(&plmn->plmn));
/* indicate New PLMN */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
@@ -1133,10 +1115,9 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
i = 0;
llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
/* if last selected PLMN was HPLMN, we skip that */
- if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
- subscr->imsi)
- && plmn_entry->mcc == plmn->mcc
- && plmn_entry->mnc == plmn->mnc) {
+ if (gsm_match_mnc(plmn_entry->plmn.mcc, plmn_entry->plmn.mnc,
+ plmn_entry->plmn.mnc_3_digits, subscr->imsi)
+ && (osmo_plmn_cmp(&plmn_entry->plmn, &plmn->plmn) == 0)) {
LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
"previously selected.\n");
i++;
@@ -1147,10 +1128,9 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
plmn_first = plmn_entry;
break;
}
- LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
+ LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc-mnc=%s), "
"because it is not allowed (cause %d).\n", i,
- gsm_print_mcc(plmn_entry->mcc),
- gsm_print_mnc(plmn_entry->mnc),
+ osmo_plmn_name(&plmn_entry->plmn),
plmn_entry->cause);
i++;
}
@@ -1164,15 +1144,14 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
- "mnc=%s %s, %s)\n", plmn->plmn_curr,
- gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
- gsm_get_mcc(plmn_first->mcc),
- gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
+ LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc-mnc=%s %s, %s)\n",
+ plmn->plmn_curr,
+ osmo_plmn_name(&plmn_first->plmn),
+ gsm_get_mcc(plmn_first->plmn.mcc),
+ gsm_get_mnc(&plmn_first->plmn));
/* set current network */
- plmn->mcc = plmn_first->mcc;
- plmn->mnc = plmn_first->mnc;
+ memcpy(&plmn->plmn, &plmn_first->plmn, sizeof(struct osmo_plmn_id));
new_a_state(plmn, GSM322_A3_TRYING_PLMN);
@@ -1208,10 +1187,9 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
plmn_next = plmn_entry;
break;
}
- LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), "
+ LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc-mnc=%s), "
"because it is not allowed (cause %d).\n", i,
- gsm_print_mcc(plmn_entry->mcc),
- gsm_print_mnc(plmn_entry->mnc),
+ osmo_plmn_name(&plmn_entry->plmn),
plmn_entry->cause);
i++;
}
@@ -1225,13 +1203,13 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
}
/* set next network */
- plmn->mcc = plmn_next->mcc;
- plmn->mnc = plmn_next->mnc;
+ memcpy(&plmn->plmn, &plmn_next->plmn, sizeof(struct osmo_plmn_id));
- LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
- "mnc=%s %s, %s)\n", plmn->plmn_curr,
- gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
- gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+ LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc-mnc=%s %s, %s)\n",
+ plmn->plmn_curr,
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
new_a_state(plmn, GSM322_A3_TRYING_PLMN);
@@ -1268,8 +1246,7 @@ static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
/* search current PLMN in list */
llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
- if (plmn_entry->mcc == plmn->mcc
- && plmn_entry->mnc == plmn->mnc) {
+ if (osmo_plmn_cmp(&plmn_entry->plmn, &plmn->plmn) == 0) {
plmn_found = plmn_entry;
break;
}
@@ -1305,8 +1282,8 @@ static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_subscriber *subscr = &ms->subscr;
struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
- if (subscr->plmn_valid && plmn->mcc == gm->mcc
- && plmn->mnc == gm->mnc) {
+ if (subscr->plmn_valid &&
+ (osmo_plmn_cmp(&plmn->plmn, &gm->plmn) == 0)) {
struct msgb *nmsg;
new_m_state(plmn, GSM322_A1_TRYING_RPLMN);
@@ -1337,17 +1314,14 @@ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
int found;
/* any allowable PLMN available */
- found = gsm322_cs_select(ms, -1, 0, 0, 0);
+ found = gsm322_cs_select(ms, -1, NULL, 0);
/* if PLMN in list */
if (found >= 0) {
- LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
- "%s, %s)\n", gsm_print_mcc(
- cs->list[found].sysinfo->mcc),
- gsm_print_mnc(cs->list[found].sysinfo->mnc),
- gsm_get_mcc(cs->list[found].sysinfo->mcc),
- gsm_get_mnc(cs->list[found].sysinfo->mcc,
- cs->list[found].sysinfo->mnc));
+ LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&cs->list[found].sysinfo->lai.plmn),
+ gsm_get_mcc(cs->list[found].sysinfo->lai.plmn.mcc),
+ gsm_get_mnc(&cs->list[found].sysinfo->lai.plmn));
return gsm322_a_sel_first_plmn(ms, msg);
}
@@ -1374,17 +1348,17 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
/* if there is a registered PLMN */
if (subscr->plmn_valid) {
/* select the registered PLMN */
- plmn->mcc = subscr->plmn_mcc;
- plmn->mnc = subscr->plmn_mnc;
+ memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id));
LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
- "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
- gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
- LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
- "%s, %s)\n", gsm_print_mcc(plmn->mcc),
- gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ "(mcc-%s %s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
+ LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
@@ -1397,7 +1371,7 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- plmn->mcc = plmn->mnc = 0;
+ memset(&plmn->plmn, 0, sizeof(plmn->plmn));
/* initiate search at cell selection */
LOGP(DSUM, LOGL_INFO, "Search for network\n");
@@ -1440,7 +1414,7 @@ static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
gsm322_cs_sendmsg(ms, nmsg);
/* flush list of PLMNs */
- gsm_subscr_del_forbidden_plmn(&ms->subscr, 0, 0);
+ gsm_subscr_del_forbidden_plmn(&ms->subscr, NULL);
return gsm322_a_switch_on(ms, msg);
}
@@ -1515,41 +1489,37 @@ static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
/* generate list */
gsm322_sort_list(ms);
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
switch (msg_type) {
case GSM322_EVENT_REG_FAILED:
- vty_notify(ms, "Failed to register to network %s, %s "
- "(%s, %s)\n",
- gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
- gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ l23_vty_ms_notify(ms, "Failed to register to network %s (%s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
break;
case GSM322_EVENT_NO_CELL_FOUND:
- vty_notify(ms, "No cell found for network %s, %s "
- "(%s, %s)\n",
- gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
- gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ l23_vty_ms_notify(ms, "No cell found for network %s (%s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
break;
case GSM322_EVENT_ROAMING_NA:
- vty_notify(ms, "Roaming not allowed to network %s, %s "
- "(%s, %s)\n",
- gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
- gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ l23_vty_ms_notify(ms, "Roaming not allowed to network %s (%s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
break;
}
if (llist_empty(&plmn->sorted_plmn))
- vty_notify(ms, "Search network!\n");
+ l23_vty_ms_notify(ms, "Search network!\n");
else {
- vty_notify(ms, "Search or select from network:\n");
+ l23_vty_ms_notify(ms, "Search or select from network:\n");
llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
- vty_notify(ms, " Network %s, %s (%s, %s)\n",
- gsm_print_mcc(temp->mcc),
- gsm_print_mnc(temp->mnc),
- gsm_get_mcc(temp->mcc),
- gsm_get_mnc(temp->mcc, temp->mnc));
+ l23_vty_ms_notify(ms, " Network mcc-mnc=%s (%s, %s)\n",
+ osmo_plmn_name(&temp->plmn),
+ gsm_get_mcc(temp->plmn.mcc),
+ gsm_get_mnc(&temp->plmn));
}
/* go Not on PLMN state */
@@ -1582,7 +1552,7 @@ static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
* selected by the user. this prevents from switching back to the
* last selected PLMN and destroying the list of scanned networks.
*/
- plmn->mcc = plmn->mnc = 0;
+ memset(&plmn->plmn, 0, sizeof(plmn->plmn));
if (!subscr->sim_valid) {
return 0;
@@ -1634,17 +1604,16 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
/* select the registered PLMN */
- plmn->mcc = subscr->plmn_mcc;
- plmn->mnc = subscr->plmn_mnc;
+ memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id));
- LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
- "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
- gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
- LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
- "%s, %s)\n", gsm_print_mcc(plmn->mcc),
- gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
+ LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
@@ -1657,7 +1626,7 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- plmn->mcc = plmn->mnc = 0;
+ memset(&plmn->plmn, 0, sizeof(plmn->plmn));
/* initiate search at cell selection */
LOGP(DSUM, LOGL_INFO, "Search for network\n");
@@ -1704,7 +1673,7 @@ static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
gsm322_cs_sendmsg(ms, nmsg);
/* flush list of PLMNs */
- gsm_subscr_del_forbidden_plmn(&ms->subscr, 0, 0);
+ gsm_subscr_del_forbidden_plmn(&ms->subscr, NULL);
return gsm322_m_switch_on(ms, msg);
}
@@ -1716,9 +1685,8 @@ static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_subscriber *subscr = &ms->subscr;
/* set last registered PLMN */
- subscr->plmn_valid = 1;
- subscr->plmn_mcc = plmn->mcc;
- subscr->plmn_mnc = plmn->mnc;
+ subscr->plmn_valid = true;
+ memcpy(&subscr->plmn, &plmn->plmn, sizeof(struct osmo_plmn_id));
new_m_state(plmn, GSM322_M2_ON_PLMN);
@@ -1753,15 +1721,15 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
/* use user selection */
- plmn->mcc = gm->mcc;
- plmn->mnc = gm->mnc;
+ memcpy(&plmn->plmn, &gm->plmn, sizeof(struct osmo_plmn_id));
- LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
- "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
- gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+ LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&plmn->plmn),
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
/* if selected PLMN is in list of forbidden PLMNs */
- gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
+ gsm_subscr_del_forbidden_plmn(subscr, &plmn->plmn);
new_m_state(plmn, GSM322_M4_TRYING_PLMN);
@@ -1817,8 +1785,7 @@ static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
*/
/* select a suitable and allowable cell */
-static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc,
- uint16_t mnc, int any)
+static int gsm322_cs_select(struct osmocom_ms *ms, int index, const struct osmo_plmn_id *plmn, int any)
{
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm_settings *set = &ms->settings;
@@ -1906,58 +1873,51 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc,
if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
if (!any) {
LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is "
- "in list of forbidden LAs. (mcc=%s "
- "mnc=%s lai=%04x)\n",
+ "in list of forbidden LAs. (lai=%s)\n",
gsm_print_arfcn(index2arfcn(i)),
- gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc), s->lac);
+ osmo_lai_name(&s->lai));
continue;
}
LOGP(DCS, LOGL_INFO, "Accept ARFCN %s: Cell is in "
"list of forbidden LAs, but we search for any "
- "cell. (mcc=%s mnc=%s lai=%04x)\n",
+ "cell. (lai=%s)\n",
gsm_print_arfcn(index2arfcn(i)),
- gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc), s->lac);
+ osmo_lai_name(&s->lai));
cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
}
/* if cell is in list of forbidden PLMNs */
- if (gsm_subscr_is_forbidden_plmn(subscr, s->mcc, s->mnc)) {
+ if (gsm_subscr_is_forbidden_plmn(subscr, &s->lai.plmn)) {
if (!any) {
LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is "
- "in list of forbidden PLMNs. (mcc=%s "
- "mnc=%s)\n",
+ "in list of forbidden PLMNs. (mcc-mnc=%s)\n",
gsm_print_arfcn(index2arfcn(i)),
- gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc));
+ osmo_plmn_name(&s->lai.plmn));
continue;
}
LOGP(DCS, LOGL_INFO, "Accept ARFCN %s: Cell is in list "
"of forbidden PLMNs, but we search for any "
- "cell. (mcc=%s mnc=%s)\n",
+ "cell. (mcc-mnc=%s)\n",
gsm_print_arfcn(index2arfcn(i)),
- gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
+ osmo_plmn_name(&s->lai.plmn));
cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
}
/* if we search a specific PLMN, but it does not match */
- if (!any && mcc && (mcc != s->mcc
- || mnc != s->mnc)) {
+ if (!any && plmn && (osmo_plmn_cmp(&s->lai.plmn, plmn) != 0)) {
LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: PLMN of cell "
- "does not match target PLMN. (mcc=%s "
- "mnc=%s)\n", gsm_print_arfcn(index2arfcn(i)),
- gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc));
+ "does not match target PLMN. (mcc-mnc=%s)\n",
+ gsm_print_arfcn(index2arfcn(i)),
+ osmo_plmn_name(&s->lai.plmn));
continue;
}
LOGP(DCS, LOGL_INFO, "Cell ARFCN %s: Cell found, (rxlev=%s "
- "mcc=%s mnc=%s lac=%04x %s, %s)\n",
- gsm_print_arfcn(index2arfcn(i)),
+ "lai=%s %s, %s)\n",
+ gsm_print_arfcn(index2arfcn(i)),
gsm_print_rxlev(cs->list[i].rxlev),
- gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
- gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
+ osmo_lai_name(&s->lai),
+ gsm_get_mcc(s->lai.plmn.mcc), gsm_get_mnc(&s->lai.plmn));
/* find highest power cell */
if (found < 0 || cs->list[i].rxlev > power) {
@@ -1974,8 +1934,7 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc,
}
/* re-select a suitable and allowable cell */
-static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc,
- uint16_t mnc, int any)
+static int gsm322_cs_reselect(struct osmocom_ms *ms, const struct osmo_plmn_id *plmn, int any)
{
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm_subscriber *subscr = &ms->subscr;
@@ -2004,18 +1963,18 @@ static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc,
/* if cell is in list of forbidden LAs */
if (!any && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in list of "
- "forbidden LAs. (mcc=%s mnc=%s lai=%04x)\n",
- gsm_print_arfcn(index2arfcn(i)), gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc), s->lac);
+ "forbidden LAs. (lai=%s)\n",
+ gsm_print_arfcn(index2arfcn(i)),
+ osmo_lai_name(&s->lai));
return -1;
}
/* if cell is in list of forbidden PLMNs */
- if (!any && gsm_subscr_is_forbidden_plmn(subscr, s->mcc, s->mnc)) {
+ if (!any && gsm_subscr_is_forbidden_plmn(subscr, &s->lai.plmn)) {
LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in "
- "list of forbidden PLMNs. (mcc=%s mnc=%s)\n",
+ "list of forbidden PLMNs. (mcc-mnc=%s)\n",
gsm_print_arfcn(index2arfcn(i)),
- gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc));
+ osmo_plmn_name(&s->lai.plmn));
return -1;
}
@@ -2030,21 +1989,21 @@ static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc,
}
/* if we search a specific PLMN, but it does not match */
- if (!any && mcc && (mcc != s->mcc
- || mnc != s->mnc)) {
+ if (!any && plmn && (osmo_plmn_cmp(plmn, &s->lai.plmn) != 0)) {
LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: PLMN of cell "
- "does not match target PLMN. (mcc=%s mnc=%s)\n",
- gsm_print_arfcn(index2arfcn(i)), gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc));
+ "does not match target PLMN. (mcc-mnc=%s)\n",
+ gsm_print_arfcn(index2arfcn(i)),
+ osmo_plmn_name(&s->lai.plmn));
return -1;
}
LOGP(DCS, LOGL_INFO, "Cell ARFCN %s: Neighbour cell accepted, "
- "(rxlev=%s mcc=%s mnc=%s lac=%04x %s, %s)\n",
+ "(rxlev=%s lai=%s %s, %s)\n",
gsm_print_arfcn(index2arfcn(i)),
gsm_print_rxlev(cs->list[i].rxlev),
- gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac,
- gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
+ osmo_lai_name(&s->lai),
+ gsm_get_mcc(s->lai.plmn.mcc),
+ gsm_get_mnc(&s->lai.plmn));
return i;
}
@@ -2053,12 +2012,13 @@ static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc,
static int gsm322_search_end(struct osmocom_ms *ms)
{
struct gsm322_cellsel *cs = &ms->cellsel;
- struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_plmn *plmn322 = &ms->plmn;
struct msgb *nmsg;
struct gsm322_msg *ngm;
int msg_type = -1; /* no message to be sent */
- int tune_back = 0, mcc = 0, mnc = 0;
+ int tune_back = 0;
int found;
+ struct osmo_plmn_id plmn = {};
switch (cs->state) {
case GSM322_ANY_SEARCH:
@@ -2066,12 +2026,12 @@ static int gsm322_search_end(struct osmocom_ms *ms)
LOGP(DCS, LOGL_INFO, "Any cell search finished.\n");
/* create AA flag */
- found = gsm322_cs_select(ms, -1, 0, 0, 0);
+ found = gsm322_cs_select(ms, -1, NULL, 0);
/* if no cell is found, or if we don't wait for any available
* and allowable PLMN to appear, we just continue to camp */
if (ms->settings.plmn_mode != PLMN_MODE_AUTO
- || plmn->state != GSM322_A4_WAIT_FOR_PLMN
+ || plmn322->state != GSM322_A4_WAIT_FOR_PLMN
|| found < 0) {
tune_back = 1;
gsm322_c_camp_any_cell(ms, NULL);
@@ -2080,10 +2040,9 @@ static int gsm322_search_end(struct osmocom_ms *ms)
/* indicate available PLMN, include selected PLMN, if found */
msg_type = GSM322_EVENT_PLMN_AVAIL;
- if (gsm322_is_plmn_avail_and_allow(cs, plmn->mcc, plmn->mnc)) {
+ if (gsm322_is_plmn_avail_and_allow(cs, &plmn322->plmn)) {
/* set what PLMN becomes available */
- mcc = plmn->mcc;
- mnc = plmn->mnc;
+ memcpy(&plmn, &plmn322->plmn, sizeof(struct osmo_plmn_id));
}
new_c_state(cs, GSM322_C0_NULL);
@@ -2096,7 +2055,7 @@ static int gsm322_search_end(struct osmocom_ms *ms)
LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
/* create AA flag */
- gsm322_cs_select(ms, -1, 0, 0, 0);
+ gsm322_cs_select(ms, -1, NULL, 0);
new_c_state(cs, GSM322_C0_NULL);
@@ -2148,8 +2107,7 @@ static int gsm322_search_end(struct osmocom_ms *ms)
if (!nmsg)
return -ENOMEM;
ngm = (struct gsm322_msg *) nmsg->data;
- ngm->mcc = mcc;
- ngm->mnc = mnc;
+ memcpy(&ngm->plmn, &plmn, sizeof(struct osmo_plmn_id));
gsm322_plmn_sendmsg(ms, nmsg);
}
@@ -2166,10 +2124,8 @@ static int gsm322_search_end(struct osmocom_ms *ms)
memcpy(cs->list[cs->arfci].sysinfo, &cs->sel_si,
sizeof(struct gsm48_sysinfo));
cs->si = cs->list[cs->arfci].sysinfo;
- cs->sel_mcc = cs->si->mcc;
- cs->sel_mnc = cs->si->mnc;
- cs->sel_lac = cs->si->lac;
- cs->sel_id = cs->si->cell_id;
+ cs->sel_cgi.lai = cs->si->lai;
+ cs->sel_cgi.cell_identity = cs->si->cell_id;
LOGP(DCS, LOGL_INFO, "Tuning back to frequency %s after full "
"search.\n", gsm_print_arfcn(cs->arfcn));
cs->sync_retries = SYNC_RETRIES;
@@ -2317,17 +2273,17 @@ static int gsm322_cs_store(struct osmocom_ms *ms)
cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_BARRED;
/* store selected network */
- if (s->mcc) {
- if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
+ if (s->lai.plmn.mcc) {
+ if (gsm322_is_forbidden_la(ms, &s->lai))
cs->list[cs->arfci].flags |= GSM322_CS_FLAG_FORBIDD;
else
cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_FORBIDD;
}
- LOGP(DCS, LOGL_DEBUG, "Scan frequency %s: Cell found. (rxlev %s "
- "mcc %s mnc %s lac %04x)\n", gsm_print_arfcn(cs->arfcn),
+ LOGP(DCS, LOGL_DEBUG, "Scan frequency %s: Cell found. (rxlev %s lai %s)\n",
+ gsm_print_arfcn(cs->arfcn),
gsm_print_rxlev(cs->list[cs->arfci].rxlev),
- gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac);
+ osmo_lai_name(&s->lai));
/* selected PLMN (auto) becomes available during "any search" */
if (ms->settings.plmn_mode == PLMN_MODE_AUTO
@@ -2336,10 +2292,10 @@ static int gsm322_cs_store(struct osmocom_ms *ms)
|| cs->state == GSM322_C8_ANY_CELL_RESEL
|| cs->state == GSM322_C9_CHOOSE_ANY_CELL)
&& plmn->state == GSM322_A4_WAIT_FOR_PLMN
- && s->mcc == plmn->mcc && s->mnc == plmn->mnc) {
+ && (osmo_plmn_cmp(&s->lai.plmn, &plmn->plmn) == 0)) {
LOGP(DCS, LOGL_INFO, "Candidate network to become available "
"again\n");
- found = gsm322_cs_select(ms, cs->arfci, s->mcc, s->mnc, 0);
+ found = gsm322_cs_select(ms, cs->arfci, &s->lai.plmn, 0);
if (found >= 0) {
LOGP(DCS, LOGL_INFO, "Selected PLMN in \"A4_WAIT_F"
"OR_PLMN\" state becomes available.\n");
@@ -2350,8 +2306,7 @@ indicate_plmn_avail:
return -ENOMEM;
/* set what PLMN becomes available */
ngm = (struct gsm322_msg *) nmsg->data;
- ngm->mcc = plmn->mcc;
- ngm->mnc = plmn->mcc;
+ memcpy(&ngm->plmn, &plmn->plmn, sizeof(struct osmo_plmn_id));
gsm322_plmn_sendmsg(ms, nmsg);
new_c_state(cs, GSM322_C0_NULL);
@@ -2367,10 +2322,10 @@ indicate_plmn_avail:
|| cs->state == GSM322_C8_ANY_CELL_RESEL
|| cs->state == GSM322_C9_CHOOSE_ANY_CELL)
&& plmn->state == GSM322_M3_NOT_ON_PLMN
- && s->mcc == plmn->mcc && s->mnc == plmn->mnc) {
+ && (osmo_plmn_cmp(&s->lai.plmn, &plmn->plmn) == 0)) {
LOGP(DCS, LOGL_INFO, "Candidate network to become available "
"again\n");
- found = gsm322_cs_select(ms, cs->arfci, s->mcc, s->mnc, 0);
+ found = gsm322_cs_select(ms, cs->arfci, &s->lai.plmn, 0);
if (found >= 0) {
LOGP(DCS, LOGL_INFO, "Current selected PLMN in \"M3_N"
"OT_ON_PLMN\" state becomes available.\n");
@@ -2410,9 +2365,9 @@ indicate_plmn_avail:
if (cs->state == GSM322_C4_NORMAL_CELL_RESEL
|| cs->state == GSM322_C8_ANY_CELL_RESEL)
- found = gsm322_cs_reselect(ms, cs->mcc, cs->mnc, any);
+ found = gsm322_cs_reselect(ms, &cs->plmn, any);
else
- found = gsm322_cs_select(ms, -1, cs->mcc, cs->mnc, any);
+ found = gsm322_cs_select(ms, -1, &cs->plmn, any);
/* if not found */
if (found < 0) {
@@ -2437,18 +2392,14 @@ indicate_plmn_avail:
cs->selected = 1;
cs->sel_arfcn = cs->arfcn;
memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
- cs->sel_mcc = cs->si->mcc;
- cs->sel_mnc = cs->si->mnc;
- cs->sel_lac = cs->si->lac;
- cs->sel_id = cs->si->cell_id;
+ cs->sel_cgi.lai = cs->si->lai;
+ cs->sel_cgi.cell_identity = cs->si->cell_id;
if (ms->rrlayer.monitor) {
- vty_notify(ms, "MON: %scell selected ARFCN=%s MCC=%s MNC=%s "
- "LAC=0x%04x cellid=0x%04x (%s %s)\n",
+ l23_vty_ms_notify(ms, "MON: %scell selected ARFCN=%s CGI=%s (%s %s)\n",
(any) ? "any " : "", gsm_print_arfcn(cs->sel_arfcn),
- gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc),
- cs->sel_lac, cs->sel_id,
- gsm_get_mcc(cs->sel_mcc),
- gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
+ osmo_cgi_name(&cs->sel_cgi),
+ gsm_get_mcc(cs->sel_cgi.lai.plmn.mcc),
+ gsm_get_mnc(&cs->sel_cgi.lai.plmn));
}
/* tell CS process about available cell */
@@ -2468,7 +2419,8 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm48_sysinfo *s;
struct gsm322_ba_list *ba = NULL;
- int i, refer_pcs;
+ int i;
+ bool refer_pcs;
uint8_t freq[128+38];
if (!cs) {
@@ -2484,13 +2436,12 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
/* collect system information received during dedicated mode */
if (s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) {
/* find or create ba list */
- ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
+ ba = gsm322_find_ba_list(cs, &s->lai.plmn);
if (!ba) {
ba = talloc_zero(ms, struct gsm322_ba_list);
if (!ba)
return NULL;
- ba->mcc = s->mcc;
- ba->mnc = s->mnc;
+ memcpy(&ba->plmn, &s->lai.plmn, sizeof(struct osmo_plmn_id));
llist_add_tail(&ba->entry, &cs->ba_list);
}
/* update (add) ba list */
@@ -2506,10 +2457,10 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
}
}
if (!!memcmp(freq, ba->freq, sizeof(freq))) {
- LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
- "%s, %s).\n", gsm_print_mcc(ba->mcc),
- gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
- gsm_get_mnc(ba->mcc, ba->mnc));
+ LOGP(DCS, LOGL_INFO, "New BA list (mcc-mnc=%s %s, %s).\n",
+ osmo_plmn_name(&ba->plmn),
+ gsm_get_mcc(ba->plmn.mcc),
+ gsm_get_mnc(&ba->plmn));
memcpy(ba->freq, freq, sizeof(freq));
}
}
@@ -2522,17 +2473,17 @@ static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
struct gsm48_sysinfo *s)
{
struct gsm322_ba_list *ba;
- int i, refer_pcs;
+ int i;
+ bool refer_pcs;
uint8_t freq[128+38];
/* find or create ba list */
- ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
+ ba = gsm322_find_ba_list(cs, &s->lai.plmn);
if (!ba) {
ba = talloc_zero(cs->ms, struct gsm322_ba_list);
if (!ba)
return -ENOMEM;
- ba->mcc = s->mcc;
- ba->mnc = s->mnc;
+ memcpy(&ba->plmn, &s->lai.plmn, sizeof(struct osmo_plmn_id));
llist_add_tail(&ba->entry, &cs->ba_list);
}
/* update ba list */
@@ -2549,10 +2500,10 @@ static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
}
}
if (!!memcmp(freq, ba->freq, sizeof(freq))) {
- LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
- "%s, %s).\n", gsm_print_mcc(ba->mcc),
- gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
- gsm_get_mnc(ba->mcc, ba->mnc));
+ LOGP(DCS, LOGL_INFO, "New BA list (mcc-mnc=%s %s, %s).\n",
+ osmo_plmn_name(&ba->plmn),
+ gsm_get_mcc(ba->plmn.mcc),
+ gsm_get_mnc(&ba->plmn));
memcpy(ba->freq, freq, sizeof(freq));
}
@@ -2586,8 +2537,8 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
* Depending on the extended bit in the channel description,
* we require more or less system information about neighbor cells
*/
- if (s->mcc
- && s->mnc
+ if (s->lai.plmn.mcc
+ && s->lai.plmn.mnc
&& (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
|| gm->sysinfo == GSM48_MT_RR_SYSINFO_2
|| gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
@@ -2627,7 +2578,7 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
&& cs->list[cs->arfci].sysinfo->sp_cbq)) {
LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: trigger cell re-selection"
+ l23_vty_ms_notify(ms, "MON: trigger cell re-selection"
": cell becomes barred\n");
trigger_resel:
/* mark cell as unscanned */
@@ -2657,27 +2608,26 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
& (s->class_barr ^ 0xffff))) {
LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: trigger cell re-selection"
+ l23_vty_ms_notify(ms, "MON: trigger cell re-selection"
": access to cell becomes barred\n");
goto trigger_resel;
}
}
/* check if MCC, MNC, LAC, cell ID changes */
- if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
- || cs->sel_lac != s->lac) {
+ if (osmo_lai_cmp(&cs->sel_cgi.lai, &s->lai) != 0) {
LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
"This is not good!\n");
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: trigger cell re-selection: "
+ l23_vty_ms_notify(ms, "MON: trigger cell re-selection: "
"cell changes LAI\n");
goto trigger_resel;
}
- if (cs->sel_id != s->cell_id) {
+ if (cs->sel_cgi.cell_identity != s->cell_id) {
LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
"This is not good!\n");
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: trigger cell re-selection: "
+ l23_vty_ms_notify(ms, "MON: trigger cell re-selection: "
"cell changes cell ID\n");
goto trigger_resel;
}
@@ -2702,8 +2652,8 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
* Depending on the extended bit in the channel description,
* we require more or less system information about neighbor cells
*/
- if (s->mcc
- && s->mnc
+ if (s->lai.plmn.mcc
+ && s->lai.plmn.mnc
&& (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
|| gm->sysinfo == GSM48_MT_RR_SYSINFO_2
|| gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
@@ -3095,7 +3045,7 @@ static void gsm322_cs_loss(void *arg)
LOGP(DCS, LOGL_INFO, "Loss of CCCH, Trigger "
"re-selection.\n");
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: trigger cell "
+ l23_vty_ms_notify(ms, "MON: trigger cell "
"re-selection: loss of signal\n");
nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
@@ -3287,7 +3237,7 @@ static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
- cs->mcc = cs->mnc = 0;
+ memset(&cs->plmn, 0, sizeof(cs->plmn));
/* unset selected cell */
gsm322_unselect_cell(cs);
@@ -3389,11 +3339,10 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
struct gsm322_cellsel *cs = &ms->cellsel;
struct msgb *nmsg;
- LOGP(DSUM, LOGL_INFO, "Camping normally on cell (ARFCN=%s mcc=%s "
- "mnc=%s %s, %s)\n", gsm_print_arfcn(cs->sel_arfcn),
- gsm_print_mcc(cs->sel_mcc),
- gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
- gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
+ LOGP(DSUM, LOGL_INFO, "Camping normally on cell (ARFCN=%s mcc-mnc=%s %s, %s)\n", gsm_print_arfcn(cs->sel_arfcn),
+ osmo_plmn_name(&cs->sel_cgi.lai.plmn),
+ gsm_get_mcc(cs->sel_cgi.lai.plmn.mcc),
+ gsm_get_mnc(&cs->sel_cgi.lai.plmn));
/* if we did cell reselection, we have a valid last serving cell */
if (cs->state != GSM322_C4_NORMAL_CELL_RESEL)
@@ -3416,11 +3365,11 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
struct gsm322_cellsel *cs = &ms->cellsel;
struct msgb *nmsg;
- LOGP(DSUM, LOGL_INFO, "Camping on any cell (ARFCN=%s mcc=%s "
- "mnc=%s %s, %s)\n", gsm_print_arfcn(cs->sel_arfcn),
- gsm_print_mcc(cs->sel_mcc),
- gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
- gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
+ LOGP(DSUM, LOGL_INFO, "Camping on any cell (ARFCN=%s mcc-mnc=%s %s, %s)\n",
+ gsm_print_arfcn(cs->sel_arfcn),
+ osmo_plmn_name(&cs->sel_cgi.lai.plmn),
+ gsm_get_mcc(cs->sel_cgi.lai.plmn.mcc),
+ gsm_get_mnc(&cs->sel_cgi.lai.plmn));
/* (re-)starting 'any cell selection' timer to look for coverage of
* allowed PLMNs.
@@ -3429,13 +3378,12 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
if (ms->subscr.sim_valid
&& (cs->state != GSM322_C8_ANY_CELL_RESEL
|| !osmo_timer_pending(&cs->any_timer))) {
- struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_plmn *plmn322 = &ms->plmn;
stop_any_timer(cs);
if (ms->settings.plmn_mode == PLMN_MODE_MANUAL
- && (!plmn->mcc
- || gsm_subscr_is_forbidden_plmn(&ms->subscr, plmn->mcc,
- plmn->mnc))) {
+ && (!plmn322->plmn.mcc
+ || gsm_subscr_is_forbidden_plmn(&ms->subscr, &plmn322->plmn))) {
LOGP(DCS, LOGL_INFO, "Not starting 'any search' timer, "
"because no selected PLMN or forbidden\n");
} else
@@ -3462,8 +3410,7 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
}
/* create temporary ba range with given frequency ranges */
-struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
- uint32_t *range, uint8_t ranges, uint8_t refer_pcs)
+static struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms, uint32_t *range, uint8_t ranges, bool refer_pcs)
{
static struct gsm322_ba_list ba;
int lower, higher;
@@ -3526,8 +3473,7 @@ static int gsm322_cs_choose(struct osmocom_ms *ms)
if (!ba) {
LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
"BA list.\n");
- ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
- cs->sel_si.mnc);
+ ba = gsm322_find_ba_list(cs, &cs->sel_si.lai.plmn);
}
}
@@ -3628,23 +3574,22 @@ static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
struct gsm322_cellsel *cs = &ms->cellsel;
- struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_plmn *plmn322 = &ms->plmn;
struct gsm322_ba_list *ba;
- cs->mcc = plmn->mcc;
- cs->mnc = plmn->mnc;
+ memcpy(&cs->plmn, &plmn322->plmn, sizeof(struct osmo_plmn_id));
if (gm->limited) {
LOGP(DCS, LOGL_INFO, "Selected PLMN with limited service.\n");
return gsm322_c_any_cell_sel(ms, msg);
}
- LOGP(DSUM, LOGL_INFO, "Selecting PLMN (mcc=%s mnc=%s %s, %s)\n",
- gsm_print_mcc(cs->mcc), gsm_print_mnc(cs->mnc),
- gsm_get_mcc(cs->mcc), gsm_get_mnc(cs->mcc, cs->mnc));
+ LOGP(DSUM, LOGL_INFO, "Selecting PLMN (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&cs->plmn),
+ gsm_get_mcc(cs->plmn.mcc), gsm_get_mnc(&cs->plmn));
/* search for BA list */
- ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
+ ba = gsm322_find_ba_list(cs, &plmn322->plmn);
if (ba) {
LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
@@ -3835,7 +3780,8 @@ static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
&& ((1 << plmn->state) & plmnastatelist[i].states))
break;
if (i == PLMNASLLEN) {
- LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
+ LOGP(DPLMN, LOGL_NOTICE, "Event %s unhandled in state %s.\n",
+ get_event_name(msg_type), get_a_state_name(plmn->state));
return 0;
}
@@ -4154,13 +4100,13 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
}
if (ms->rrlayer.monitor) {
- vty_notify(ms, "MON: cell ARFCN LAC C1 C2 CRH RLA_C "
+ l23_vty_ms_notify(ms, "MON: cell ARFCN LAC C1 C2 CRH RLA_C "
"bargraph\n");
snprintf(arfcn_text, 10, "%s ",
gsm_print_arfcn(cs->sel_arfcn));
arfcn_text[9] = '\0';
- vty_notify(ms, "MON: serving %s 0x%04x %3d %3d %4d "
- "%s\n", arfcn_text, cs->sel_lac, cs->c1, cs->c2,
+ l23_vty_ms_notify(ms, "MON: serving %s 0x%04x %3d %3d %4d "
+ "%s\n", arfcn_text, cs->sel_cgi.lai.lac, cs->c1, cs->c2,
cs->rla_c_dbm, bargraph(cs->rla_c_dbm / 2, -55, -24));
}
@@ -4181,7 +4127,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
snprintf(arfcn_text, 10, "%s ",
gsm_print_arfcn(nb->arfcn));
arfcn_text[9] = '\0';
- vty_notify(ms, "MON: nb %2d %s ARFCN not "
+ l23_vty_ms_notify(ms, "MON: nb %2d %s ARFCN not "
"supported\n", i + 1, arfcn_text);
}
goto cont;
@@ -4194,7 +4140,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
snprintf(arfcn_text, 10, "%s ",
gsm_print_arfcn(nb->arfcn));
arfcn_text[9] = '\0';
- vty_notify(ms, "MON: nb %2d %s "
+ l23_vty_ms_notify(ms, "MON: nb %2d %s "
" %4d %s\n",
i + 1, arfcn_text, nb->rla_c_dbm,
bargraph(nb->rla_c_dbm / 2, -55, -24));
@@ -4207,7 +4153,10 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
nb->prio_low = 1;
/* get C1 & C2 */
- gsm_arfcn2band_rc(nb->arfcn, &band);
+ if (gsm_arfcn2band_rc(nb->arfcn, &band) != 0) {
+ LOGP(DNB, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n");
+ goto cont;
+ }
class = class_of_band(ms, band);
nb->c1 = calculate_c1(DNB, nb->rla_c_dbm, s->rxlev_acc_min_db,
ms_pwr_dbm(band, s->ms_txpwr_max_cch),
@@ -4220,8 +4169,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
nb->c12_valid = 1;
/* calculate CRH depending on LAI */
- if (cs->sel_mcc == s->mcc && cs->sel_mnc == s->mnc
- && cs->sel_lac == s->lac) {
+ if (osmo_lai_cmp(&cs->sel_cgi.lai, &s->lai) == 0) {
LOGP(DNB, LOGL_INFO, "-> Cell of is in the same LA, "
"so CRH = 0\n");
nb->crh = 0;
@@ -4240,8 +4188,8 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
snprintf(arfcn_text, 10, "%s ",
gsm_print_arfcn(nb->arfcn));
arfcn_text[9] = '\0';
- vty_notify(ms, "MON: nb %2d %s 0x%04x %3d %3d %2d"
- " %4d %s\n", i + 1, arfcn_text, s->lac,
+ l23_vty_ms_notify(ms, "MON: nb %2d %s 0x%04x %3d %3d %2d"
+ " %4d %s\n", i + 1, arfcn_text, s->lai.lac,
nb->c1, nb->c2, nb->crh, nb->rla_c_dbm,
bargraph(nb->rla_c_dbm / 2, -55, -24));
}
@@ -4262,18 +4210,17 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
}
/* check if LA is forbidden */
- if (any && gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) {
+ if (any && gsm322_is_forbidden_la(ms, &s->lai)) {
LOGP(DNB, LOGL_INFO, "Skip cell: Cell has "
"forbidden LA.\n");
goto cont;
}
/* check if we have same PLMN */
- if (!any && (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc)) {
+ if (!any && (osmo_plmn_cmp(&cs->sel_cgi.lai.plmn, &s->lai.plmn) != 0)) {
LOGP(DNB, LOGL_INFO, "Skip cell: PLMN of cell "
- "does not match target PLMN. (cell: mcc=%s "
- "mnc=%s)\n", gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc));
+ "does not match target PLMN. (cell: mcc-mnc=%s)\n",
+ osmo_plmn_name(&s->lai.plmn));
goto cont;
}
@@ -4316,7 +4263,7 @@ cont:
if (!i) {
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: no neighbour cells\n");
+ l23_vty_ms_notify(ms, "MON: no neighbour cells\n");
}
if (cs->resel_when + GSM58_RESEL_THRESHOLD >= now) {
@@ -4457,7 +4404,8 @@ static int gsm322_nb_start(struct osmocom_ms *ms, int synced)
uint8_t map[128];
uint16_t nc[32];
uint8_t changed = 0;
- int refer_pcs, index;
+ bool refer_pcs;
+ int index;
uint16_t arfcn;
if (cs->ms->settings.no_neighbour)
@@ -4625,7 +4573,7 @@ printf("%d time to sync again: %u\n", nb->arfcn, now + GSM58_READ_AGAIN - nb->wh
"reselection.\n");
if (ms->rrlayer.monitor)
- vty_notify(ms, "MON: trigger cell re-selection: "
+ l23_vty_ms_notify(ms, "MON: trigger cell re-selection: "
"better cell\n");
cs->resel_when = now;
@@ -4707,7 +4655,11 @@ static int gsm322_nb_new_rxlev(struct gsm322_cellsel *cs)
enum gsm_band band;
int class;
- gsm_arfcn2band_rc(cs->arfcn, &band);
+ if (gsm_arfcn2band_rc(cs->arfcn, &band) != 0) {
+ LOGP(DNB, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n");
+ return -EINVAL;
+ }
+
class = class_of_band(cs->ms, band);
/* calculate the RAL_C of serving cell */
@@ -4853,9 +4805,9 @@ int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
LOGP(DPLMN, LOGL_INFO, "MCC |MNC |allowed|rx-lev\n");
LOGP(DPLMN, LOGL_INFO, "-------+-------+-------+-------\n");
llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
- LOGP(DPLMN, LOGL_INFO, "%s |%s%s |%s |%s\n",
- gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
- ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
+ LOGP(DPLMN, LOGL_INFO, "%s |%-3s |%s |%s\n",
+ osmo_mcc_name(temp->plmn.mcc),
+ osmo_mnc_name(temp->plmn.mnc, temp->plmn.mnc_3_digits),
(temp->cause) ? "no ":"yes",
gsm_print_rxlev(temp->rxlev));
}
@@ -4883,11 +4835,11 @@ int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
print(priv, "%4dDCS|", i);
else
print(priv, "%4d |", i);
- if (s->mcc) {
- print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc),
- ((s->mnc & 0x00f) == 0x00f) ? " ":"");
- print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
+ if (s->lai.plmn.mcc) {
+ print(priv, "%s |%-3s |",
+ osmo_mcc_name(s->lai.plmn.mcc),
+ osmo_mnc_name(s->lai.plmn.mnc, s->lai.plmn.mnc_3_digits));
+ print(priv, "0x%04x |0x%04x |", s->lai.lac, s->cell_id);
} else
print(priv, "n/a |n/a |n/a |n/a |");
if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
@@ -4926,27 +4878,27 @@ int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
print(priv, "MCC |MNC |LAC |cause\n");
print(priv, "-------+-------+-------+-------\n");
llist_for_each_entry(temp, &plmn->forbidden_la, entry)
- print(priv, "%s |%s%s |0x%04x |#%d\n",
- gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
- ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
- temp->lac, temp->cause);
+ print(priv, "%s |%-3s |0x%04x |#%d\n",
+ osmo_mcc_name(temp->lai.plmn.mcc),
+ osmo_mnc_name(temp->lai.plmn.mnc, temp->lai.plmn.mnc_3_digits),
+ temp->lai.lac, temp->cause);
return 0;
}
-int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
+int gsm322_dump_ba_list(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn,
void (*print)(void *, const char *, ...), void *priv)
{
struct gsm322_ba_list *ba;
int i;
llist_for_each_entry(ba, &cs->ba_list, entry) {
- if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
+ if (plmn && (osmo_plmn_cmp(&ba->plmn, plmn) != 0))
continue;
- print(priv, "Band Allocation of network: MCC %s MNC %s "
- "(%s, %s)\n", gsm_print_mcc(ba->mcc),
- gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
- gsm_get_mnc(ba->mcc, ba->mnc));
+ print(priv, "Band Allocation of network: MCC-MNC %s (%s, %s)\n",
+ osmo_plmn_name(&ba->plmn),
+ gsm_get_mcc(ba->plmn.mcc),
+ gsm_get_mnc(&ba->plmn));
for (i = 0; i <= 1023+299; i++) {
if ((ba->freq[i >> 3] & (1 << (i & 7))))
print(priv, " %s",
@@ -4976,7 +4928,7 @@ int gsm322_dump_nb_list(struct gsm322_cellsel *cs,
print(priv, "C1=%d C2=%d ", cs->c1, cs->c1);
else
print(priv, "C1 - C2 - ");
- print(priv, "LAC=0x%04x\n\n", (cs->selected) ? cs->sel_si.lac : 0);
+ print(priv, "LAC=0x%04x\n\n", (cs->selected) ? cs->sel_si.lai.lac : 0);
print(priv, "Neighbour cells:\n\n");
llist_for_each_entry(nb, &cs->nb_list, entry) {
@@ -5019,7 +4971,7 @@ int gsm322_dump_nb_list(struct gsm322_cellsel *cs,
s = cs->list[arfcn2index(nb->arfcn)].sysinfo;
if (nb->state == GSM322_NB_SYSINFO && s) {
print(priv, "%s |0x%04x |0x%04x |",
- (nb->prio_low) ? "low ":"normal", s->lac,
+ (nb->prio_low) ? "low ":"normal", s->lai.lac,
s->cell_id);
} else
print(priv, "- |- |- |");
@@ -5090,6 +5042,7 @@ int gsm322_init(struct osmocom_ms *ms)
"stored BA list becomes obsolete.\n");
} else
while(!feof(fp)) {
+ uint16_t mcc_hex, mnc_hex;
ba = talloc_zero(ms, struct gsm322_ba_list);
if (!ba) {
fclose(fp);
@@ -5100,18 +5053,30 @@ int gsm322_init(struct osmocom_ms *ms)
talloc_free(ba);
break;
}
- ba->mcc = (buf[0] << 8) | buf[1];
- ba->mnc = (buf[2] << 8) | buf[3];
+ mcc_hex = (buf[0] << 8) | buf[1];
+ mnc_hex = (buf[2] << 8) | buf[3];
+ ba->plmn.mcc = (((mcc_hex & 0x0f00) >> 8) * 100) +
+ (((mcc_hex & 0x00f0) >> 4) * 10) +
+ (mcc_hex & 0x000f);
+ ba->plmn.mnc_3_digits = ((mnc_hex & 0x00f) != 0x00f);
+ if (ba->plmn.mnc_3_digits)
+ ba->plmn.mnc = (((mnc_hex & 0x0f00) >> 8) * 100) +
+ (((mnc_hex & 0x00f0) >> 4) * 10) +
+ (mnc_hex & 0x000f);
+ else
+ ba->plmn.mnc = (((mnc_hex & 0x0f00) >> 8) * 10) +
+ (((mnc_hex & 0x00f0) >> 4));
+
rc = fread(ba->freq, sizeof(ba->freq), 1, fp);
if (!rc) {
talloc_free(ba);
break;
}
llist_add_tail(&ba->entry, &cs->ba_list);
- LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
- "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
- gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
- gsm_get_mnc(ba->mcc, ba->mnc));
+ LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&ba->plmn),
+ gsm_get_mcc(ba->plmn.mcc),
+ gsm_get_mnc(&ba->plmn));
}
fclose(fp);
} else
@@ -5120,17 +5085,63 @@ int gsm322_init(struct osmocom_ms *ms)
return 0;
}
+static void gsm322_write_ba(struct osmocom_ms *ms)
+{
+ const struct gsm322_ba_list *ba;
+ char *ba_filename;
+ FILE *fp;
+
+ ba_filename = talloc_asprintf(ms, "%s/%s.ba", config_dir, ms->name);
+ OSMO_ASSERT(ba_filename != NULL);
+
+ LOGP(DCS, LOGL_INFO, "Writing stored BA list to '%s'\n", ba_filename);
+
+ fp = fopen(ba_filename, "w");
+ talloc_free(ba_filename);
+ if (fp == NULL) {
+ LOGP(DCS, LOGL_ERROR,
+ "Failed to open '%s' for writing: %s\n",
+ ba_filename, strerror(errno));
+ return;
+ }
+
+ fputs(ba_version, fp);
+
+ llist_for_each_entry(ba, &ms->cellsel.ba_list, entry) {
+ size_t rc = 0;
+ uint16_t mcc_hex = gsm_mcc_to_hex(ba->plmn.mcc);
+ uint16_t mnc_hex = gsm_mnc_to_hex(ba->plmn.mnc, ba->plmn.mnc_3_digits);
+ uint8_t buf[] = {
+ mcc_hex >> 8, mcc_hex & 0xff,
+ mnc_hex >> 8, mnc_hex & 0xff,
+ };
+
+ LOGP(DCS, LOGL_INFO,
+ "Writing stored BA list entry (mcc-mnc=%s %s, %s)\n",
+ osmo_plmn_name(&ba->plmn),
+ gsm_get_mcc(ba->plmn.mcc),
+ gsm_get_mnc(&ba->plmn));
+
+ rc += fwrite(buf, sizeof(buf), 1, fp);
+ rc += fwrite(ba->freq, sizeof(ba->freq), 1, fp);
+
+ /* fwrite() returns count of written items, should be 2 */
+ if (rc != 2) {
+ LOGP(DCS, LOGL_ERROR,
+ "Writing stored BA list: fwrite() failed (rc=%zu)\n", rc);
+ break;
+ }
+ }
+
+ fclose(fp);
+}
+
int gsm322_exit(struct osmocom_ms *ms)
{
struct gsm322_plmn *plmn = &ms->plmn;
struct gsm322_cellsel *cs = &ms->cellsel;
struct llist_head *lh, *lh2;
struct msgb *msg;
- FILE *fp;
- char *ba_filename;
- struct gsm322_ba_list *ba;
- uint8_t buf[4];
- int rc = 0;
int i;
LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
@@ -5158,32 +5169,7 @@ int gsm322_exit(struct osmocom_ms *ms)
cs->si = NULL;
/* store BA list */
- ba_filename = talloc_asprintf(ms, "%s/%s.ba", config_dir, ms->name);
- if (ba_filename) {
- fp = fopen(ba_filename, "w");
- talloc_free(ba_filename);
- if (fp) {
- fputs(ba_version, fp);
- llist_for_each_entry(ba, &cs->ba_list, entry) {
- buf[0] = ba->mcc >> 8;
- buf[1] = ba->mcc & 0xff;
- buf[2] = ba->mnc >> 8;
- buf[3] = ba->mnc & 0xff;
-
- LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s "
- "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
- gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
- gsm_get_mnc(ba->mcc, ba->mnc));
-
- rc += fwrite(buf, 4, 1, fp);
- rc += fwrite(ba->freq, sizeof(ba->freq), 1, fp);
- }
- fclose(fp);
- }
- }
-
- if (rc != 2)
- LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
+ gsm322_write_ba(ms);
/* free lists */
while ((msg = msgb_dequeue(&plmn->event_queue)))
diff --git a/src/host/layer23/src/mobile/gsm411_sms.c b/src/host/layer23/src/mobile/gsm411_sms.c
index 593a2ad3..a21133b9 100644
--- a/src/host/layer23/src/mobile/gsm411_sms.c
+++ b/src/host/layer23/src/mobile/gsm411_sms.c
@@ -19,10 +19,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 <stdint.h>
@@ -34,9 +30,11 @@
#include <osmocom/core/msgb.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/transaction.h>
#include <osmocom/bb/mobile/gsm411_sms.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
#include <osmocom/gsm/gsm0411_utils.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bb/mobile/vty.h>
@@ -123,11 +121,11 @@ struct gsm_sms *sms_from_text(const char *receiver, int dcs, const char *text)
static int gsm411_sms_report(struct osmocom_ms *ms, struct gsm_sms *sms,
uint8_t cause)
{
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
if (!cause)
- vty_notify(ms, "SMS to %s successful\n", sms->address);
+ l23_vty_ms_notify(ms, "SMS to %s successful\n", sms->address);
else
- vty_notify(ms, "SMS to %s failed: %s\n", sms->address,
+ l23_vty_ms_notify(ms, "SMS to %s failed: %s\n", sms->address,
get_value_string(gsm411_rp_cause_strs, cause));
mobile_prim_ntfy_sms_status(ms, sms, cause);
@@ -193,8 +191,8 @@ static int sms_store(struct osmocom_ms *ms, struct msgb *msg,
if (*p == '\n' || *p == '\r')
*p = ' ';
}
- vty_notify(ms, NULL);
- vty_notify(ms, "SMS from %s: '%s'\n", gsms->address, vty_text);
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "SMS from %s: '%s'\n", gsms->address, vty_text);
home = getenv("HOME");
if (!home) {
@@ -654,6 +652,14 @@ int gsm411_tx_sms_submit(struct osmocom_ms *ms, const char *sms_sca,
return -EIO;
}
+ /* ASCI call does not allow other transactions */
+ if (trans_find_ongoing_gcc_bcc(ms)) {
+ LOGP(DLSMS, LOGL_ERROR, "Phone is busy doing ASCI call\n");
+ gsm411_sms_report(ms, sms, GSM411_RP_CAUSE_MO_TEMP_FAIL);
+ sms_free(sms);
+ return -EIO;
+ }
+
/* allocate transaction with dummy reference */
transaction_id = trans_assign_trans_id(ms, GSM48_PDISC_SMS, 0);
if (transaction_id < 0) {
@@ -917,7 +923,7 @@ int gsm411_rcv_sms(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_trans *trans;
int rc = 0;
- trans = trans_find_by_callref(ms, mmh->ref);
+ trans = trans_find_by_callref(ms, GSM48_PDISC_SMS, mmh->ref);
if (!trans) {
LOGP(DLSMS, LOGL_INFO, " -> (new transaction sapi=%d)\n", sapi);
trans = trans_alloc(ms, GSM48_PDISC_SMS, mmh->transaction_id,
diff --git a/src/host/layer23/src/mobile/gsm414.c b/src/host/layer23/src/mobile/gsm414.c
index 2f630df3..90fc2dfd 100644
--- a/src/host/layer23/src/mobile/gsm414.c
+++ b/src/host/layer23/src/mobile/gsm414.c
@@ -16,10 +16,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 <stdint.h>
@@ -37,7 +33,9 @@
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
#include <l1ctl_proto.h>
@@ -139,7 +137,7 @@ static int handle_close_tch_loop(struct osmocom_ms *ms, const struct msgb *msg)
/* Instruct the L1 to enable received TCH loopback mode
* FIXME: delay applying this mode, so we can send the ACK first */
- l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, rr->tch_loop_mode);
+ l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, rr->cd_now.tch_flags, rr->tch_loop_mode);
/* Craft and send the ACKnowledgement */
nmsg = alloc_gsm414_msg(GSM414_MT_CLOSE_TCH_LOOP_ACK);
@@ -180,7 +178,7 @@ static int handle_open_tch_loop(struct osmocom_ms *ms, const struct msgb *msg)
rsl_chan_nr_str(rr->cd_now.chan_nr), loop_mode_name(rr->tch_loop_mode));
/* Instruct the L1 to disable the TCH loopback mode */
- l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, L1CTL_TCH_LOOP_OPEN);
+ l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, rr->cd_now.tch_flags, L1CTL_TCH_LOOP_OPEN);
/* Only the loop mode C needs to be ACKnowledged */
bool needs_ack = rr->tch_loop_mode == L1CTL_TCH_LOOP_C;
diff --git a/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c b/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c
new file mode 100644
index 00000000..3a4db3a6
--- /dev/null
+++ b/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c
@@ -0,0 +1,1967 @@
+/* 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/>.
+ */
+
+/* Notes on the state machine:
+ *
+ * The state machine is different from the diagram depicted in the specs.
+ * This is because there are some messages missing and some state transitions
+ * are different or not shown.
+ *
+ * A call that has no channel is answered without joining the group channel.
+ * If it comes available, the establishment is performed and the U4 is entered.
+ *
+ * Uplink control is not described in the diagram. Talking/listening is
+ * requested by user and can be rejected by MM layer, if talking is not
+ * allowed.
+ *
+ * We can be sure that there is no other MM connection while doing VGCS call
+ * establishment: MMxx-EST-REQ is only accpepted, if there is no MM connection.
+ * We block calls from user, if there is some other transaction, which is not
+ * in state U3. Also we accept incoming indications any time and create
+ * transactions that go to state U3.
+ *
+ * If the upper layer or lower layer requests another call/SMS/SS while VGCS
+ * call is ongoing, this may cause undefined behaviour.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/protocol/gsm_44_068.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/mobile/mncc.h>
+#include <osmocom/bb/mobile/transaction.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
+#include <osmocom/bb/mobile/tch.h>
+#include <osmocom/bb/mobile/vty.h>
+#include <l1ctl_proto.h>
+
+#define S(x) (1 << (x))
+
+#define LOG_GCC(trans, level, fmt, args...) \
+ LOGP(((trans)->protocol == GSM48_PDISC_GROUP_CC) ? DGCC : DBCC, level, \
+ ((trans)->protocol == GSM48_PDISC_GROUP_CC) ? ("VGCS callref %u: " fmt) : ("VBS callref %u: " fmt), \
+ (trans)->callref, ##args)
+#define LOG_GCC_PR(protocol, ref, level, fmt, args...) \
+ LOGP((protocol == GSM48_PDISC_GROUP_CC) ? DGCC : DBCC, level, \
+ (protocol == GSM48_PDISC_GROUP_CC) ? ("VGCS callref %u: " fmt) : ("VBS callref %u: " fmt), \
+ ref, ##args)
+
+/*
+ * init
+ */
+
+int gsm44068_gcc_init(struct osmocom_ms *ms)
+{
+ LOGP(DGCC, LOGL_INFO, "init GCC/BCC\n");
+
+ return 0;
+}
+
+int gsm44068_gcc_exit(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans, *trans2;
+
+ LOGP(DGCC, LOGL_INFO, "exit GCC/BCC processes for %s\n", ms->name);
+
+ llist_for_each_entry_safe(trans, trans2, &ms->trans_list, entry) {
+ if (trans->protocol == GSM48_PDISC_GROUP_CC || trans->protocol == GSM48_PDISC_BCAST_CC) {
+ LOG_GCC(trans, LOGL_NOTICE, "Free pendig CC-transaction.\n");
+ trans_free(trans);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * messages
+ */
+
+/* TS 44.068 Chapter 6.1.2.1 */
+enum vgcs_gcc_fsm_states {
+ VGCS_GCC_ST_U0_NULL = 0,
+ VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING,
+ VGCS_GCC_ST_U1_GROUP_CALL_INITIATED,
+ VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, /* sepeate link */
+ VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, /* wait for receive mode */
+ VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, /* receive mode / U6 @ BCC */
+ VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE, /* wait for send and receive mode */
+ VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE, /* send and receive mode */
+ VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, /* no channel */
+ VGCS_GCC_ST_U3_GROUP_CALL_PRESENT,
+ VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST,
+ VGCS_GCC_ST_U5_TERMINATION_REQUESTED,
+};
+
+/* TS 44.068 Figure 6.1 (additional events added) */
+enum vgcs_gcc_fsm_event {
+ VGCS_GCC_EV_SETUP_REQ, /* calling user initiates call */
+ VGCS_GCC_EV_TERM_REQ, /* calling user requests termination */
+ VGCS_GCC_EV_MM_EST_CNF, /* MM connection established */
+ VGCS_GCC_EV_MM_EST_REJ, /* MM connection failed */
+ VGCS_GCC_EV_DI_TERMINATION, /* network acknowledges termination */
+ VGCS_GCC_EV_DI_TERM_REJECT, /* network rejects termination */
+ VGCS_GCC_EV_DI_CONNECT, /* network indicates connect */
+ VGCS_GCC_EV_TIMEOUT, /* several timeout events */
+ VGCS_GCC_EV_SETUP_IND, /* notification of ongoing call received */
+ VGCS_GCC_EV_REL_IND, /* notification of call being gone */
+ VGCS_GCC_EV_JOIN_GC_REQ, /* user wants to join ongoing call */
+ VGCS_GCC_EV_JOIN_GC_CNF, /* MM confirms joining ongoing call */
+ VGCS_GCC_EV_ABORT_REQ, /* user rejects or leaves call */
+ VGCS_GCC_EV_ABORT_IND, /* MM indicates channel released or failed */
+ VGCS_GCC_EV_TALK_REQ, /* user wants to talk */
+ VGCS_GCC_EV_TALK_CNF, /* MM confirms talk */
+ VGCS_GCC_EV_TALK_REJ, /* MM rejects talk */
+ VGCS_GCC_EV_LISTEN_REQ, /* user wants to listen */
+ VGCS_GCC_EV_LISTEN_CNF, /* MM confirms listen */
+ VGCS_GCC_EV_MM_IDLE, /* MM layer becomes ready for new channel */
+ VGCS_GCC_EV_UPLINK_FREE, /* MM layer indicates free uplink in group receive mode */
+ VGCS_GCC_EV_UPLINK_BUSY, /* MM layer indicates busy uplink in group receive mode */
+};
+
+static const struct value_string vgcs_gcc_fsm_event_names[] = {
+ OSMO_VALUE_STRING(VGCS_GCC_EV_SETUP_REQ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_TERM_REQ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_MM_EST_CNF),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_MM_EST_REJ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_DI_TERMINATION),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_DI_TERM_REJECT),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_DI_CONNECT),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_TIMEOUT),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_SETUP_IND),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_REL_IND),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_JOIN_GC_REQ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_JOIN_GC_CNF),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_ABORT_REQ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_ABORT_IND),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_REQ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_CNF),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_REJ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_LISTEN_REQ),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_LISTEN_CNF),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_MM_IDLE),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_UPLINK_FREE),
+ OSMO_VALUE_STRING(VGCS_GCC_EV_UPLINK_BUSY),
+ { }
+};
+
+/*! return string representation of GCC/BCC Message Type */
+static const char *gsm44068_gcc_msg_name(uint8_t msg_type)
+{
+ return get_value_string(osmo_gsm44068_msg_type_names, msg_type);
+}
+
+#define TFU(param) ((param < 0) ? "unchanged" : ((param) ? "T" : "F"))
+
+/* Set state attributes and check if they are consistent with the current state. */
+static int set_state_attributes(struct gsm_trans *trans, int d_att, int u_att, int comm, int orig, int call_state)
+{
+ bool orig_t = false, comm_t = false;
+
+ LOG_GCC(trans, LOGL_DEBUG, "Setting state attributes: D-ATT = %s, U-ATT = %s, COMM = %s, ORIG = %s.\n",
+ TFU(d_att), TFU(u_att), TFU(comm), TFU(orig));
+
+ /* Control Speaker. */
+ if (d_att >= 0 && trans->gcc.d_att != d_att) {
+ LOG_GCC(trans, LOGL_DEBUG, "Switching Speaker to %d\n", d_att);
+ gsm48_rr_audio_mode(trans->ms, AUDIO_TX_MICROPHONE | (d_att * AUDIO_RX_SPEAKER));
+ }
+
+ if (d_att >= 0)
+ trans->gcc.d_att = d_att;
+ if (u_att >= 0)
+ trans->gcc.u_att = u_att;
+ if (comm >= 0)
+ trans->gcc.comm = comm;
+ if (orig >= 0)
+ trans->gcc.orig = orig;
+ if (call_state >= 0)
+ trans->gcc.call_state = call_state;
+
+ switch (trans->gcc.fi->state) {
+ case VGCS_GCC_ST_U3_GROUP_CALL_PRESENT:
+ case VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST:
+ orig_t = orig;
+ comm_t = comm;
+ break;
+ case VGCS_GCC_ST_U0_NULL:
+ case VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE:
+ case VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE:
+ comm_t = comm;
+ break;
+ }
+
+ if (orig_t)
+ LOG_GCC(trans, LOGL_ERROR, "ORIG = T is inconsistent with states U3 and U4. Please fix!");
+
+ if (comm_t)
+ LOG_GCC(trans, LOGL_ERROR,
+ "COMM = T is inconsistent with states U0, U3, U4, U2nc and U2r. Please fix!");
+
+ return (orig_t || comm_t) ? -EINVAL : 0;
+}
+
+static void vgcs_vty_notify(struct gsm_trans *trans, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+static void vgcs_vty_notify(struct gsm_trans *trans, const char *fmt, ...)
+{
+ struct osmocom_ms *ms = trans->ms;
+ char buffer[1000];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "%s call %d: %s", (trans->protocol == GSM48_PDISC_GROUP_CC) ? "Group" : "Broadcast",
+ trans->callref, buffer);
+}
+
+/*
+ * messages
+ */
+
+/* Send MMxx-GROUP-REQ to MM. */
+static int vgcs_group_req(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+ uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_GROUP_REQ : GSM48_MMBCC_GROUP_REQ;
+
+ LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type));
+
+ OSMO_ASSERT(trans->gcc.ch_desc_present);
+
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *) nmsg->data;
+ nmmh->ch_desc_present = trans->gcc.ch_desc_present;
+ memcpy(&nmmh->ch_desc, &trans->gcc.ch_desc, sizeof(nmmh->ch_desc));
+
+ return gsm48_mmxx_downmsg(trans->ms, nmsg);
+}
+
+/* Send MMxx-EST-REQ to MM. */
+static int vgcs_est_req(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+ uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_EST_REQ : GSM48_MMBCC_EST_REQ;
+
+ LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type));
+
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+ return gsm48_mmxx_downmsg(trans->ms, nmsg);
+}
+
+/* Push message and send MMxx-DATA-REQ to MM. */
+static int vgcs_data_req(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_mmxx_hdr *mmh;
+ uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_DATA_REQ : GSM48_MMBCC_DATA_REQ;
+
+ /* push RR header */
+ msg->l3h = msg->data;
+ msgb_push(msg, sizeof(struct gsm48_mmxx_hdr));
+ mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ mmh->msg_type = msg_type;
+ mmh->ref = trans->callref;
+ mmh->transaction_id = trans->transaction_id;
+ mmh->sapi = 0;
+
+ /* send message to MM */
+ return gsm48_mmxx_downmsg(trans->ms, msg);
+}
+
+/* Send MMxx-REL-REQ to MM. */
+static int vgcs_rel_req(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+ uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_REL_REQ : GSM48_MMBCC_REL_REQ;
+
+ LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type));
+
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+ return gsm48_mmxx_downmsg(trans->ms, nmsg);
+}
+
+/* Send MMxx-UPLINK-REQ to MM. */
+static int vgcs_uplink_req(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+ uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_UPLINK_REQ : GSM48_MMBCC_UPLINK_REQ;
+
+ LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type));
+
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+ return gsm48_mmxx_downmsg(trans->ms, nmsg);
+}
+
+/* Send MMxx-UPLINK-REL-REQ to MM. */
+static int vgcs_uplink_rel_req(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+ uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_UPLINK_REL_REQ
+ : GSM48_MMBCC_UPLINK_REL_REQ;
+
+ LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type));
+
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+ return gsm48_mmxx_downmsg(trans->ms, nmsg);
+}
+
+static void _add_callref_ie(struct msgb *msg, uint32_t callref, bool with_prio, uint8_t prio)
+{
+ uint32_t ie;
+
+ ie = callref << 5;
+ if (with_prio)
+ ie |= 0x10 | (prio << 1);
+ msgb_put_u32(msg, ie);
+}
+
+static void _add_user_user_ie(struct msgb *msg, uint8_t user_pdisc, uint8_t *user, uint8_t user_len)
+{
+ uint8_t *ie;
+
+ ie = msgb_put(msg, user_len + 2);
+ *ie++ = GSM48_IE_USER_USER;
+ *ie++ = user_len;
+ memcpy(ie, user, user_len);
+}
+
+#define GSM44068_IE_CALL_STATE 0xA0
+#define GSM44068_IE_STATE_ATTRS 0xB0
+#define GSM44068_IE_TALKER_PRIO 0xc0
+
+static void _add_call_state_ie(struct msgb *msg, uint8_t call_state)
+{
+ msgb_put_u8(msg, GSM44068_IE_CALL_STATE | call_state);
+}
+
+static void _add_state_attrs_ie(struct msgb *msg, uint8_t da, uint8_t ua, uint8_t comm, uint8_t oi)
+{
+ msgb_put_u8(msg, GSM44068_IE_STATE_ATTRS | (da << 3) | (ua << 2) | (comm << 1) | oi);
+}
+
+static void _add_talker_prio_ie(struct msgb *msg, uint8_t talker_prio)
+{
+ msgb_put_u8(msg, GSM44068_IE_TALKER_PRIO | talker_prio);
+}
+
+static void _add_cause_ie(struct msgb *msg, uint8_t cause, uint8_t *diag, uint8_t diag_len)
+{
+ uint8_t *ie;
+
+ ie = msgb_put(msg, diag_len + 2);
+ *ie++ = diag_len + 1;
+ *ie++ = 0x80 | cause;
+ if (diag_len && diag)
+ memcpy(ie, diag, diag_len);
+}
+
+static int _msg_too_short(struct gsm_trans *trans)
+{
+ LOG_GCC(trans, LOGL_ERROR, "MSG too short.\n");
+ return -EINVAL;
+}
+
+static int _ie_invalid(struct gsm_trans *trans)
+{
+ LOG_GCC(trans, LOGL_ERROR, "IE invalid.\n");
+ return -EINVAL;
+}
+
+/* 3GPP TS 44.068 Clause 8.4 */
+static int gsm44068_rx_set_parameter(struct gsm_trans *trans, struct msgb *msg,
+ uint8_t *da, uint8_t *ua, uint8_t *comm, uint8_t *oi)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int remaining_len = msgb_l3len(msg) - sizeof(*gh);
+ uint8_t *ie = gh->data;
+
+ /* State attributes */
+ if (remaining_len < 1)
+ return _msg_too_short(trans);
+ if (da)
+ *da = (ie[0] >> 3) & 0x1;
+ if (ua)
+ *ua = (ie[0] >> 2) & 0x1;
+ if (comm)
+ *comm = (ie[0] >> 1) & 0x1;
+ if (oi)
+ *oi = ie[0] & 0x1;
+ ie += 1;
+
+ return 0;
+}
+
+/* 3GPP TS 44.068 Clause 8.5 */
+static int gsm44068_tx_setup(struct gsm_trans *trans, uint32_t callref, bool with_prio, uint8_t prio,
+ uint8_t user_pdisc, uint8_t *user, uint8_t user_len,
+ bool with_talker_prio, uint8_t talker_prio)
+{
+ struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX SETUP");
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ LOG_GCC(trans, LOGL_INFO, "Sending SETUP.\n");
+
+ gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
+ gh->msg_type = OSMO_GSM44068_MSGT_SETUP;
+ _add_callref_ie(msg, callref, with_prio, prio);
+ if (user_len && user)
+ _add_user_user_ie(msg, user_pdisc, user, user_len);
+ if (with_talker_prio)
+ _add_talker_prio_ie(msg, talker_prio);
+
+ return vgcs_data_req(trans, msg);
+}
+
+/* 3GPP TS 44.068 Clause 8.6 */
+static int gsm44068_tx_status(struct gsm_trans *trans, uint8_t cause, uint8_t *diag, uint8_t diag_len,
+ bool with_call_state, uint8_t call_state, bool with_state_attrs,
+ uint8_t da, uint8_t ua, uint8_t comm, uint8_t oi)
+{
+ struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX STATUS");
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ LOG_GCC(trans, LOGL_INFO, "Sending STATUS.\n");
+
+ gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
+ gh->msg_type = OSMO_GSM44068_MSGT_STATUS;
+ _add_cause_ie(msg, cause, diag, diag_len);
+ if (with_call_state)
+ _add_call_state_ie(msg, call_state);
+ if (with_state_attrs)
+ _add_state_attrs_ie(msg, da, ua, comm, oi);
+
+ return vgcs_data_req(trans, msg);
+}
+
+/* 3GPP TS 44.068 Clause 8.7 and 8.8 */
+static int gsm44068_rx_termination(struct gsm_trans *trans, struct msgb *msg, uint8_t *cause, uint8_t *diag, uint8_t *diag_len)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int remaining_len = msgb_l3len(msg) - sizeof(*gh);
+ uint8_t *ie = gh->data;
+ uint8_t ie_len;
+
+ /* Cause */
+ if (remaining_len < 2 || ie[0] < remaining_len - 2)
+ return _msg_too_short(trans);
+ ie_len = ie[0];
+ if (remaining_len < ie_len + 1)
+ return _msg_too_short(trans);
+ if (ie_len < 1)
+ return _ie_invalid(trans);
+ if (cause)
+ *cause = ie[1] & 0x7f;
+ if (diag && diag_len) {
+ *diag_len = ie_len - 1;
+ if (*diag_len)
+ memcpy(diag, ie + 2, ie_len - 1);
+ }
+ remaining_len -= ie_len + 1;
+ ie += ie_len + 1;
+
+ return 0;
+}
+
+/* 3GPP TS 44.068 Clause 8.9 */
+static int gsm44068_tx_termination_request(struct gsm_trans *trans, uint32_t callref, bool with_prio, uint8_t prio,
+ bool with_talker_prio, uint8_t talker_prio)
+{
+ struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX TERMINATION REQUEST");
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ LOG_GCC(trans, LOGL_INFO, "Sending TERMINATION REQUEST.\n");
+
+ gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
+ gh->msg_type = OSMO_GSM44068_MSGT_TERMINATION_REQUEST;
+ _add_callref_ie(msg, callref, with_prio, prio);
+ if (with_talker_prio)
+ _add_talker_prio_ie(msg, talker_prio);
+
+ return vgcs_data_req(trans, msg);
+}
+
+/* 3GPP TS 44.018 Clause 9.1.48 */
+static int gsm44068_tx_uplink_release(struct gsm_trans *trans, uint8_t cause)
+{
+ struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX UPLINK RELEASE");
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *) msgb_put(msg, sizeof(*ur));
+
+ LOG_GCC(trans, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause);
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE;
+ ur->rr_cause = cause;
+
+ return vgcs_data_req(trans, msg);
+}
+
+/*
+ * GCC/BCC state machine
+ *
+ * For reference see Figure 6.1 of TS 44.068.
+ *
+ * Note: There are some events that are not depicted in the state diagram:
+ *
+ * "L: ABORT-IND" indicates closing/failing of radio channel.
+ * "H: TALK-REQ" request talking on the channel.
+ * "L: TALK-CNF" confirms talker.
+ * "L: TALK-REJ" rejects talker.
+ * "H: LISTEN-REQ" request listening.
+ * "L: LISTEN-CNF" confirms listening.
+ *
+ */
+
+/* Table 6.1 of TS 44.068 */
+#define T_NO_CHANNEL 3
+#define T_MM_EST 7
+#define T_TERM 10
+#define T_CONN_REQ 10
+
+static void vgcs_gcc_fsm_u0_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U0);
+}
+
+static void vgcs_gcc_fsm_u0_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ switch (event) {
+ case VGCS_GCC_EV_SETUP_REQ:
+ /* The calling user initiates a new call. */
+ LOG_GCC(trans, LOGL_INFO, "Received call from user.\n");
+ /* Change to MM CONNECTION PENDING state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING, 0, 0);
+ /* Send EST-REQ to MM layer. */
+ vgcs_est_req(trans);
+ break;
+ case VGCS_GCC_EV_SETUP_IND:
+ /* New call notification. */
+ LOG_GCC(trans, LOGL_INFO, "Received call from network.\n");
+ /* Change to GROUP CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ /* Notify call at VTY. */
+ vgcs_vty_notify(trans, "Incoming call\n");
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u0p_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 0, 0, 0, 1, OSMO_GSM44068_CSTATE_U0p);
+
+ /* Start timer */
+ osmo_timer_schedule(&fi->timer, T_MM_EST, 0);
+}
+
+static void vgcs_gcc_fsm_u0p_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+ int rc;
+
+ switch (event) {
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The user terminates the call. */
+ LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Free transaction. MM confirmation/rejection is handled without transaction also. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_MM_EST_REJ:
+ /* The MM layer rejects the call. */
+ LOG_GCC(trans, LOGL_INFO, "Call was rejected by MM layer.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify reject at VTY. */
+ vgcs_vty_notify(trans, "Rejected (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_MM_EST_CNF:
+ /* The MM connection was confirmed. */
+ LOG_GCC(trans, LOGL_INFO, "Call was confirmed by MM layer.\n");
+ /* Change to GROUP CALL INITIATED state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U1_GROUP_CALL_INITIATED, 0, 0);
+ /* Choose transaction ID. */
+ rc = trans_assign_trans_id(trans->ms, trans->protocol, 0);
+ if (rc < 0) {
+ /* No free transaction ID. */
+ trans_free(trans);
+ return;
+ }
+ trans->transaction_id = rc;
+ /* Send SETUP towards network. */
+ gsm44068_tx_setup(trans, trans->callref, false, 0, false, NULL, 0, false, 0);
+ break;
+ case VGCS_GCC_EV_TIMEOUT:
+ /* Establishment of MM layer timed out. */
+ LOG_GCC(trans, LOGL_INFO, "MM layer timed out.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Free transaction. MM confirmation/rejection is handled without transaction also. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u1_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 0, 0, 1, 1, OSMO_GSM44068_CSTATE_U1);
+}
+
+static void vgcs_gcc_fsm_u1_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_DI_CONNECT:
+ /* Received CONNECT from network. */
+ LOG_GCC(trans, LOGL_INFO, "Call was accepted by network.\n");
+ /* Change to GROUP CALL ACTIVE (separate link) state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify connect at VTY. */
+ vgcs_vty_notify(trans, "Connect\n");
+ break;
+ case VGCS_GCC_EV_DI_TERMINATION:
+ /* Received TERMINATION from network. */
+ LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause);
+ /* Chane to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause);
+ /* Release MM connection. */
+ vgcs_rel_req(trans);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The user terminates the call. */
+ LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n");
+ /* Change to TERMINATION REQUESTED state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0);
+ /* Send TERMINATION REQUEST towards network. */
+ gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* Radio link was released or failed. */
+ LOG_GCC(trans, LOGL_INFO, "Got release from MM layer.\n");
+ /* Chane to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Release MM connection. */
+ vgcs_rel_req(trans);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u2sl_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 1, 1, 1, 1, OSMO_GSM44068_CSTATE_U2sl_U2);
+}
+
+static void vgcs_gcc_fsm_u2sl_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_DI_TERMINATION:
+ /* Received TERMINATION from network. */
+ LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause);
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause);
+ /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The user terminates the call. */
+ LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n");
+ /* Change to TERMINATION REQUESTED state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0);
+ /* Send TERMINATION REQUEST towards network. */
+ gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* Radio link was released or failed. */
+ LOG_GCC(trans, LOGL_INFO, "Got release from MM layer.\n");
+ /* Chane to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_LISTEN_REQ:
+ /* The user wants to release dedicated link and join the group channel as listener. */
+ LOG_GCC(trans, LOGL_INFO, "User releases uplink on dedicated channel.\n");
+ /* Change state to ACTIVE (wait receive). */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, 0, 0);
+ /* Set flag that we change to group receive mode after separate link. */
+ trans->gcc.receive_after_sl = true;
+ /* Request release of the uplink. */
+ gsm44068_tx_uplink_release(trans, GSM48_RR_CAUSE_LEAVE_GROUP_CA);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u2wr_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 1, 0, 1, -1, OSMO_GSM44068_CSTATE_U2wr_U6);
+}
+
+static void vgcs_gcc_fsm_u2wr_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_LISTEN_CNF:
+ /* The MM layer confirms uplink release. */
+ LOG_GCC(trans, LOGL_INFO, "Uplink is now released.\n");
+ /* Change to CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify answer at VTY. */
+ vgcs_vty_notify(trans, "Listening\n");
+ break;
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The calling subscriber wants to terminate the call. */
+ LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n");
+ trans->gcc.termination = true;
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User aborts group channel. */
+ LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n");
+ /* Change to GROUP CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ /* Reset flag after we changed to group receive mode after separate link. */
+ trans->gcc.receive_after_sl = false;
+ /* Notify leaving at VTY. */
+ vgcs_vty_notify(trans, "Call left\n");
+ /* Release MM connection. (Leave call.) */
+ vgcs_rel_req(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* The MM layer released the group channel. */
+ LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n");
+ if (trans->gcc.receive_after_sl) {
+ LOG_GCC(trans, LOGL_INFO, "Ignoring release, because we released separate link.\n");
+ break;
+ }
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_MM_IDLE:
+ if (!trans->gcc.receive_after_sl)
+ break;
+ /* If no channel is available (no notification received), enter the U2nc state. */
+ if (!trans->gcc.ch_desc_present) {
+ /* Change state to ACTIVE (no channel). */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify answer at VTY. */
+ vgcs_vty_notify(trans, "Listen (no channel yet)\n");
+ break;
+ }
+ /* The MM layer indicates that the phone is ready to request group channel. */
+ LOG_GCC(trans, LOGL_INFO, "MM is now idle, we can request group channel.\n");
+ /* Send GROUP-REQ to MM layer. */
+ vgcs_group_req(trans);
+ break;
+ case VGCS_GCC_EV_JOIN_GC_CNF:
+ /* Reset flag after we changed to group receive mode after separate link. */
+ trans->gcc.receive_after_sl = false;
+ /* The MM layer confirms group channel. */
+ LOG_GCC(trans, LOGL_INFO, "Joined group call after releasing separate link.\n");
+ /* Change to CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify answer at VTY. */
+ vgcs_vty_notify(trans, "Listening\n");
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u2r_u6_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 1, 0, 0, -1, (trans->protocol == GSM48_PDISC_GROUP_CC) ?
+ OSMO_GSM44068_CSTATE_U2r : OSMO_GSM44068_CSTATE_U2wr_U6);
+
+ /* There is a pending termination request, request uplink. */
+ if (trans->gcc.termination)
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REQ, NULL);
+}
+
+static void vgcs_gcc_fsm_u2r_u6_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The calling subscriber wants to terminate the call. */
+ LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n");
+ trans->gcc.termination = true;
+ /* fall-thru */
+ case VGCS_GCC_EV_TALK_REQ:
+ /* The user wants to talk. */
+ LOG_GCC(trans, LOGL_INFO, "User wants to talk on the uplink.\n");
+ /* Change to GROUP CALL ACTIVE (wait send) state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE, 0, 0);
+ /* Request group transmit mode from MM layer. */
+ vgcs_uplink_req(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User aborts group channel. */
+ LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n");
+ /* Change to GROUP CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ /* Notify leaving at VTY. */
+ vgcs_vty_notify(trans, "Call left\n");
+ /* Release MM connection. (Leave call.) */
+ vgcs_rel_req(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* The MM layer released the group channel. */
+ LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_UPLINK_FREE:
+ /* The MM layer indicates that the uplink is free. */
+ LOG_GCC(trans, LOGL_INFO, "Uplink free indication received from MM layer.\n");
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Uplink free => You may talk\n");
+ break;
+ case VGCS_GCC_EV_UPLINK_BUSY:
+ /* The MM layer indicates that the uplink is busy. */
+ LOG_GCC(trans, LOGL_INFO, "Uplink busy indication received from MM layer.\n");
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Uplink busy => You cannot talk\n");
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u2ws_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 1, 1, 0, -1, OSMO_GSM44068_CSTATE_U2ws);
+}
+
+static void vgcs_gcc_fsm_u2ws_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_TALK_CNF:
+ /* Uplink was granted. */
+ LOG_GCC(trans, LOGL_INFO, "Uplink established, user can talk now.\n");
+ /* Change to GROUP CALL ACTIVE (send and receive) state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Talking\n");
+ break;
+ case VGCS_GCC_EV_TALK_REJ:
+ /* Uplink was rejected. */
+ LOG_GCC(trans, LOGL_INFO, "Uplink rejected, user cannot talk.\n");
+ /* Clear termination flag, if set. */
+ trans->gcc.termination = false;
+ /* Change to GROUP CALL ACTIVE (receive) state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Talking rejected (cause %d)\n", *cause);
+ break;
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The calling subscriber wants to terminate the call. */
+ LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n");
+ trans->gcc.termination = true;
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User aborts group channel. */
+ LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n");
+ /* Change to GROUP CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ /* Notify leaving at VTY. */
+ vgcs_vty_notify(trans, "Call left\n");
+ /* Release MM connection. (Leave call.) */
+ vgcs_rel_req(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* The MM layer released the group channel. */
+ LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u2sr_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 1, 1, 1, -1, OSMO_GSM44068_CSTATE_U2sr);
+
+ /* There is a pending termination request, request uplink. */
+ if (trans->gcc.termination)
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TERM_REQ, NULL);
+}
+
+static void vgcs_gcc_fsm_u2sr_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_DI_TERMINATION:
+ /* Received TERMINATION from network. */
+ LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause);
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause);
+ /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_TALK_REJ:
+ /* Uplink was rejected by network. (Reject after granting access.) */
+ LOG_GCC(trans, LOGL_INFO, "Uplink rejected, user cannot talk.\n");
+ /* Change to GROUP CALL ACTIVE (receive) state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Talking rejected (cause %d)\n", *cause);
+ break;
+ case VGCS_GCC_EV_LISTEN_REQ:
+ /* The user wants to release the uplink and become a listener. */
+ LOG_GCC(trans, LOGL_INFO, "User wants to release the uplink.\n");
+ /* Change to GROUP CALL ACTIVE (wait receive) state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, 0, 0);
+ /* Request group receive mode from MM layer. */
+ vgcs_uplink_rel_req(trans);
+ break;
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The user terminates the call. */
+ LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n");
+ /* Change to TERMINATION REQUESTED state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0);
+ /* Send TERMINATION REQUEST towards network. */
+ gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0);
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User aborts group channel. */
+ LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n");
+ /* Change to GROUP CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ /* Notify leaving at VTY. */
+ vgcs_vty_notify(trans, "Call left\n");
+ /* Release MM connection. (Leave call.) */
+ vgcs_rel_req(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* The MM layer released the group channel. */
+ LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u2nc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 1, 1, 0, -1, OSMO_GSM44068_CSTATE_U2nc);
+
+ /* Start timer */
+ osmo_timer_schedule(&fi->timer, T_NO_CHANNEL, 0);
+}
+
+static void vgcs_gcc_fsm_u2nc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ switch (event) {
+ case VGCS_GCC_EV_SETUP_IND:
+ /* Channel becomes available, now join the group call. */
+ LOG_GCC(trans, LOGL_INFO, "Radio channel becomes available.\n");
+ /* Change to CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, 0, 0);
+ /* Send GROUP-REQ to MM layer. */
+ vgcs_group_req(trans);
+ break;
+ case VGCS_GCC_EV_TERM_REQ:
+ /* The calling subscriber wants to terminate the call. */
+ LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n");
+ trans->gcc.termination = true;
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User aborts group channel. */
+ LOG_GCC(trans, LOGL_INFO, "User leaves the group channel, that is not yet established.\n");
+ /* Change to GROUP CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ break;
+ case VGCS_GCC_EV_REL_IND:
+ /* The MM layer indicates that group channel is gone. */
+ LOG_GCC(trans, LOGL_INFO, "Group call notification is gone.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released\n");
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_TIMEOUT:
+ /* Group channel did not become availblet. */
+ LOG_GCC(trans, LOGL_INFO, "Timeout waiting for group channel.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Timeout\n");
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u3_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U3);
+}
+
+static void vgcs_gcc_fsm_u3_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ switch (event) {
+ case VGCS_GCC_EV_JOIN_GC_REQ:
+ /* Join (answer) incoming group call. */
+ LOG_GCC(trans, LOGL_INFO, "Join call.\n");
+ /* If no channel is available, enter the U2nc state. */
+ if (!trans->gcc.ch_desc_present) {
+ /* Change state to ACTIVE (no channel). */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify answer at VTY. */
+ vgcs_vty_notify(trans, "Answer (no channel yet)\n");
+ break;
+ }
+ /* Change to CALL PRESENT state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, 0, 0);
+ /* Send GROUP-REQ to MM layer. */
+ vgcs_group_req(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User rejects group call. */
+ LOG_GCC(trans, LOGL_INFO, "User rejects group call.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_REL_IND:
+ /* The notified call is gone. */
+ LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released\n");
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u4_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U4);
+
+ /* Start timer */
+ osmo_timer_schedule(&fi->timer, T_CONN_REQ, 0);
+}
+
+static void vgcs_gcc_fsm_u4_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_JOIN_GC_CNF:
+ /* The MM layer confirms the group receive mode. (We have a channel.) */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0);
+ /* Notify answer at VTY. */
+ vgcs_vty_notify(trans, "Answer\n");
+ break;
+ case VGCS_GCC_EV_ABORT_REQ:
+ /* User aborts group channel. */
+ LOG_GCC(trans, LOGL_INFO, "User aborts group channel.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_REL_IND:
+ /* The notified call is gone. */
+ LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released\n");
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* The notified call is gone. */
+ LOG_GCC(trans, LOGL_INFO, "Call rejected.\n");
+ /* Change back to U3 state, so that call may be joined later. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ break;
+ case VGCS_GCC_EV_TIMEOUT:
+ /* Group channel timed out. */
+ LOG_GCC(trans, LOGL_INFO, "Timeout waiting for group channel.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Timeout\n");
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void vgcs_gcc_fsm_u5_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_trans *trans = fi->priv;
+
+ set_state_attributes(trans, -1, -1, 1, 1, OSMO_GSM44068_CSTATE_U5);
+
+ /* Start timer */
+ osmo_timer_schedule(&fi->timer, T_TERM, 0);
+}
+
+static void vgcs_gcc_fsm_u5_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_trans *trans = fi->priv;
+ uint8_t *cause = data;
+
+ switch (event) {
+ case VGCS_GCC_EV_DI_TERMINATION:
+ /* The network confirm the termination. */
+ LOG_GCC(trans, LOGL_INFO, "Termination confirmend by network.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause);
+ /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_DI_TERM_REJECT:
+ /* Termination was rejected. */
+ LOG_GCC(trans, LOGL_INFO, "Termination rejected (cause %d), releasing MM connection.\n", *cause);
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Termination rejected (cause %d), Call left\n", *cause);
+ /* Release MM connection. (Leave call.)
+ * We must release here, because we can have a dedicated MM connection or joined a group channel.
+ * We cannot go back, because we don't know what state we had before U5 state was entered.
+ * Do we really need to go back or just leave the call? */
+ vgcs_rel_req(trans);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_ABORT_IND:
+ /* The notified call is gone. */
+ LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Released (cause %d)\n", *cause);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ case VGCS_GCC_EV_TIMEOUT:
+ /* Termination timed out. */
+ LOG_GCC(trans, LOGL_INFO, "Timeout waiting for termination.\n");
+ /* Change to NULL state. */
+ osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0);
+ /* Notify termination at VTY. */
+ vgcs_vty_notify(trans, "Timeout\n");
+ /* Release MM connection. (Leave call.) */
+ vgcs_rel_req(trans);
+ /* Free transaction. */
+ trans_free(trans);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int vgcs_gcc_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ return osmo_fsm_inst_dispatch(fi, VGCS_GCC_EV_TIMEOUT, NULL);
+}
+
+static const struct osmo_fsm_state vgcs_gcc_fsm_states[] = {
+ [VGCS_GCC_ST_U0_NULL] = {
+ .name = "NULL (U0)",
+ .in_event_mask = S(VGCS_GCC_EV_SETUP_REQ) |
+ S(VGCS_GCC_EV_SETUP_IND),
+ .out_state_mask = S(VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING) |
+ S(VGCS_GCC_ST_U1_GROUP_CALL_INITIATED) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT),
+ .onenter = vgcs_gcc_fsm_u0_onenter,
+ .action = vgcs_gcc_fsm_u0_action,
+ },
+ [VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING] = {
+ .name = "MM CONNECTION PENDING (U0.p)",
+ .in_event_mask = S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_MM_EST_REJ) |
+ S(VGCS_GCC_EV_MM_EST_CNF) |
+ S(VGCS_GCC_EV_TIMEOUT),
+ .out_state_mask = S(VGCS_GCC_ST_U0_NULL) |
+ S(VGCS_GCC_ST_U1_GROUP_CALL_INITIATED),
+ .onenter = vgcs_gcc_fsm_u0p_onenter,
+ .action = vgcs_gcc_fsm_u0p_action,
+ },
+ [VGCS_GCC_ST_U1_GROUP_CALL_INITIATED] = {
+ .name = "GROUP CALL INITIATED (U1)",
+ .in_event_mask = S(VGCS_GCC_EV_DI_CONNECT) |
+ S(VGCS_GCC_EV_DI_TERMINATION) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_IND),
+ .out_state_mask = S(VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U5_TERMINATION_REQUESTED) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u1_onenter,
+ .action = vgcs_gcc_fsm_u1_action,
+ },
+ [VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE] = {
+ .name = "GROUP CALL ACTIVE separate link (U2sl)",
+ .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_IND) |
+ S(VGCS_GCC_EV_LISTEN_REQ),
+ .out_state_mask = S(VGCS_GCC_ST_U0_NULL) |
+ S(VGCS_GCC_ST_U5_TERMINATION_REQUESTED) |
+ S(VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE),
+ .onenter = vgcs_gcc_fsm_u2sl_onenter,
+ .action = vgcs_gcc_fsm_u2sl_action,
+ },
+ [VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE] = {
+ .name = "GROUP CALL ACTIVE wait for receive mode (U2wr)",
+ .in_event_mask = S(VGCS_GCC_EV_LISTEN_CNF) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_ABORT_IND) |
+ S(VGCS_GCC_EV_MM_IDLE) |
+ S(VGCS_GCC_EV_JOIN_GC_CNF),
+ .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u2wr_onenter,
+ .action = vgcs_gcc_fsm_u2wr_action,
+ },
+ [VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE] = {
+ .name = "GROUP CALL ACTIVE receive mode (U2r or U6 @ BCC)",
+ .in_event_mask = S(VGCS_GCC_EV_TALK_REQ) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_ABORT_IND) |
+ S(VGCS_GCC_EV_UPLINK_FREE) |
+ S(VGCS_GCC_EV_UPLINK_BUSY),
+ .out_state_mask = S(VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u2r_u6_onenter,
+ .action = vgcs_gcc_fsm_u2r_u6_action,
+ },
+ [VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE] = {
+ .name = "GROUP CALL ACTIVE wait for send+receive mode (U2ws)",
+ .in_event_mask = S(VGCS_GCC_EV_TALK_CNF) |
+ S(VGCS_GCC_EV_TALK_REJ) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_ABORT_IND),
+ .out_state_mask = S(VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u2ws_onenter,
+ .action = vgcs_gcc_fsm_u2ws_action,
+ },
+ [VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE] = {
+ .name = "GROUP CALL ACTIVE send+receive mode (U2sr)",
+ .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) |
+ S(VGCS_GCC_EV_LISTEN_REQ) |
+ S(VGCS_GCC_EV_TALK_REJ) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_ABORT_IND),
+ .out_state_mask = S(VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u2sr_onenter,
+ .action = vgcs_gcc_fsm_u2sr_action,
+ },
+ [VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE] = {
+ .name = "GROUP CALL ACTIVE no channel (U2nc)",
+ .in_event_mask = S(VGCS_GCC_EV_SETUP_IND) |
+ S(VGCS_GCC_EV_TERM_REQ) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_REL_IND) |
+ S(VGCS_GCC_EV_TIMEOUT),
+ .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u2nc_onenter,
+ .action = vgcs_gcc_fsm_u2nc_action,
+ },
+ [VGCS_GCC_ST_U3_GROUP_CALL_PRESENT] = {
+ .name = "GROUP CALL PRESENT (U3)",
+ .in_event_mask = S(VGCS_GCC_EV_JOIN_GC_REQ) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_REL_IND),
+ .out_state_mask = S(VGCS_GCC_ST_U0_NULL) |
+ S(VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST) |
+ S(VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE),
+ .onenter = vgcs_gcc_fsm_u3_onenter,
+ .action = vgcs_gcc_fsm_u3_action,
+ },
+ [VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST] = {
+ .name = "GROUP CALL CONNECTION REQUEST (U4)",
+ .in_event_mask = S(VGCS_GCC_EV_JOIN_GC_CNF) |
+ S(VGCS_GCC_EV_ABORT_REQ) |
+ S(VGCS_GCC_EV_REL_IND) |
+ S(VGCS_GCC_EV_ABORT_IND) |
+ S(VGCS_GCC_EV_TIMEOUT),
+ .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) |
+ S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) |
+ S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u4_onenter,
+ .action = vgcs_gcc_fsm_u4_action,
+ },
+ [VGCS_GCC_ST_U5_TERMINATION_REQUESTED] = {
+ .name = "TERMINATION REQUESTED (U5)",
+ .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) |
+ S(VGCS_GCC_EV_DI_TERM_REJECT) |
+ S(VGCS_GCC_EV_ABORT_IND) |
+ S(VGCS_GCC_EV_TIMEOUT),
+ .out_state_mask = S(VGCS_GCC_ST_U0_NULL),
+ .onenter = vgcs_gcc_fsm_u5_onenter,
+ .action = vgcs_gcc_fsm_u5_action,
+ },
+};
+
+static struct osmo_fsm vgcs_gcc_fsm = {
+ .name = "gcc",
+ .states = vgcs_gcc_fsm_states,
+ .num_states = ARRAY_SIZE(vgcs_gcc_fsm_states),
+ .log_subsys = DGCC,
+ .event_names = vgcs_gcc_fsm_event_names,
+ .timer_cb = vgcs_gcc_fsm_timer_cb,
+};
+
+static struct osmo_fsm vgcs_bcc_fsm = {
+ .name = "bcc",
+ .states = vgcs_gcc_fsm_states,
+ .num_states = ARRAY_SIZE(vgcs_gcc_fsm_states),
+ .log_subsys = DBCC,
+ .event_names = vgcs_gcc_fsm_event_names,
+ .timer_cb = vgcs_gcc_fsm_timer_cb,
+};
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&vgcs_gcc_fsm) == 0);
+ OSMO_ASSERT(osmo_fsm_register(&vgcs_bcc_fsm) == 0);
+}
+
+static const char *gsm44068_gcc_state_name(struct osmo_fsm_inst *fi)
+{
+ return vgcs_gcc_fsm_states[fi->state].name;
+}
+
+/*
+ * transaction
+ */
+
+/* Create transaction together with state machine and set initail states. */
+static struct gsm_trans *trans_alloc_vgcs(struct osmocom_ms *ms, uint8_t pdisc, uint8_t transaction_id,
+ uint32_t callref)
+{
+ struct gsm_trans *trans;
+
+ trans = trans_alloc(ms, pdisc, 0xff, callref);
+ if (!trans)
+ return NULL;
+ trans->gcc.fi = osmo_fsm_inst_alloc((pdisc == GSM48_PDISC_GROUP_CC) ? &vgcs_gcc_fsm : &vgcs_bcc_fsm,
+ trans, trans, LOGL_DEBUG, NULL);
+ if (!trans->gcc.fi) {
+ trans_free(trans);
+ return NULL;
+ }
+
+ LOG_GCC(trans, LOGL_DEBUG, "Created transaction for callref %d, pdisc %d, transaction_id 0x%x\n",
+ callref, pdisc, transaction_id);
+
+ set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U0);
+
+ return trans;
+}
+
+/* Call Control Specific transaction release.
+ * gets called by trans_free, DO NOT CALL YOURSELF!
+ */
+void _gsm44068_gcc_bcc_trans_free(struct gsm_trans *trans)
+{
+ if (!trans->gcc.fi)
+ return;
+
+ /* Free state machine. */
+ osmo_fsm_inst_free(trans->gcc.fi);
+}
+
+/* Find ongoing call. (The call must not be present.) */
+struct gsm_trans *trans_find_ongoing_gcc_bcc(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans;
+
+ llist_for_each_entry(trans, &ms->trans_list, entry) {
+ if (trans->protocol != GSM48_PDISC_GROUP_CC && trans->protocol != GSM48_PDISC_BCAST_CC)
+ continue;
+ if (trans->gcc.fi->state != VGCS_GCC_ST_U3_GROUP_CALL_PRESENT)
+ return trans;
+ }
+
+ return NULL;
+}
+
+/*
+ * messages from upper/lower layers
+ */
+
+static int gsm44068_gcc_data_ind(struct gsm_trans *trans, struct msgb *msg, uint8_t transaction_id)
+{
+ struct osmocom_ms *ms = trans->ms;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ int msg_type = gh->msg_type & 0xbf;
+ int rc = 0;
+ uint8_t cause = 0;
+ uint8_t d_att, u_att, comm, orig;
+
+ /* pull the MMCC header */
+ msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
+
+ /* Use transaction ID from message. If we join a group call, we don't have it until now. */
+ trans->transaction_id = transaction_id;
+
+ LOG_GCC(trans, LOGL_INFO, "(ms %s) Received '%s' in state %s\n", ms->name, gsm44068_gcc_msg_name(msg_type),
+ gsm44068_gcc_state_name(trans->gcc.fi));
+
+ switch (msg_type) {
+ case OSMO_GSM44068_MSGT_CONNECT:
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_CONNECT, NULL);
+ break;
+ case OSMO_GSM44068_MSGT_TERMINATION:
+ rc = gsm44068_rx_termination(trans, msg, &cause, NULL, NULL);
+ if (rc < 0)
+ break;
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_TERMINATION, &cause);
+ break;
+ case OSMO_GSM44068_MSGT_TERMINATION_REJECT:
+ rc = gsm44068_rx_termination(trans, msg, &cause, NULL, NULL);
+ if (rc < 0)
+ break;
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_TERM_REJECT, &cause);
+ break;
+ case OSMO_GSM44068_MSGT_GET_STATUS:
+ /* Note: The message via UNIT DATA is not supported. */
+ gsm44068_tx_status(trans, OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS, NULL, 0,
+ true, trans->gcc.call_state,
+ true, trans->gcc.d_att, trans->gcc.u_att, trans->gcc.comm, trans->gcc.orig);
+ break;
+ case OSMO_GSM44068_MSGT_SET_PARAMETER:
+ rc = gsm44068_rx_set_parameter(trans, msg, &d_att, &u_att, &comm, &orig);
+ if (rc >= 0)
+ set_state_attributes(trans, d_att, u_att, comm, orig, -1);
+ break;
+ default:
+ LOG_GCC(trans, LOGL_NOTICE, "Message '%s' not supported.\n", gsm44068_gcc_msg_name(msg_type));
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+/* receive message from MM layer */
+int gsm44068_rcv_gcc_bcc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_settings *set = &ms->settings;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ int msg_type = mmh->msg_type;
+ uint8_t pdisc;
+ struct gsm_trans *trans;
+ int rc = 0;
+
+ /* Check for message class and get protocol type. */
+ switch ((msg_type & GSM48_MMXX_MASK)) {
+ case GSM48_MMGCC_CLASS:
+ pdisc = GSM48_PDISC_GROUP_CC;
+ if (!set->vgcs) {
+ LOGP(DGCC, LOGL_ERROR, "Ignoring message '%s', because VGCS is not supported!\n",
+ get_mmxx_name(msg_type));
+ return -ENOTSUP;
+ }
+ break;
+ case GSM48_MMBCC_CLASS:
+ pdisc = GSM48_PDISC_BCAST_CC;
+ if (!set->vbs) {
+ LOGP(DGCC, LOGL_ERROR, "Ignoring message '%s', because VBS is not supported!\n",
+ get_mmxx_name(msg_type));
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ LOGP(DGCC, LOGL_ERROR, "Message class not allowed, please fix!\n");
+ return -EINVAL;
+ }
+
+ trans = trans_find_by_callref(ms, pdisc, mmh->ref);
+ if (!trans) {
+ LOG_GCC_PR(pdisc, mmh->ref, LOGL_INFO, "(ms %s) Received '%s' without transaction.\n",
+ ms->name, get_mmxx_name(msg_type));
+
+ if (msg_type == GSM48_MMGCC_REL_IND || msg_type == GSM48_MMBCC_REL_IND) {
+ /* If we got the MMxx-EST-REJ after we aborted the call, we ignore. */
+ return 0;
+ }
+ if (msg_type == GSM48_MMGCC_EST_CNF || msg_type == GSM48_MMBCC_EST_CNF ||
+ msg_type == GSM48_MMGCC_GROUP_CNF || msg_type == GSM48_MMBCC_GROUP_CNF) {
+ struct msgb *nmsg;
+ LOG_GCC_PR(pdisc, mmh->ref, LOGL_ERROR, "Received confirm for GCC/BCC call, "
+ "but there is no transaction, releasing.\n");
+ /* If we got the MMxx-EST-CNF after we aborted the call, we release. */
+ msg_type = (pdisc == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_REL_REQ : GSM48_MMBCC_REL_REQ;
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, 0);
+ if (!nmsg)
+ return -ENOMEM;
+ LOG_GCC_PR(pdisc, mmh->ref, LOGL_INFO, "Sending %s\n", get_mmxx_name(msg_type));
+ gsm48_mmxx_downmsg(ms, nmsg);
+ return 0;
+
+ } else if (msg_type == GSM48_MMGCC_NOTIF_IND || msg_type == GSM48_MMBCC_NOTIF_IND) {
+ /* Notification gone but no transaction. */
+ if (mmh->notify != MMXX_NOTIFY_SETUP)
+ return 0;
+ /* Incoming notification, creation transaction. */
+ trans = trans_alloc_vgcs(ms, pdisc, 0xff, mmh->ref);
+ if (!trans)
+ return -ENOMEM;
+ } else {
+ LOG_GCC_PR(pdisc, mmh->ref, LOGL_ERROR, "Received GCC/BCC message for unknown transaction.\n");
+ return -ENOENT;
+ }
+ }
+
+ LOG_GCC(trans, LOGL_INFO, "(ms %s) Received '%s' in state %s\n", ms->name, get_mmxx_name(msg_type),
+ gsm44068_gcc_state_name(trans->gcc.fi));
+
+ switch (msg_type) {
+ case GSM48_MMGCC_EST_CNF:
+ case GSM48_MMBCC_EST_CNF:
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_EST_CNF, NULL);
+ break;
+ case GSM48_MMGCC_ERR_IND:
+ case GSM48_MMBCC_ERR_IND:
+ case GSM48_MMGCC_REL_IND:
+ case GSM48_MMBCC_REL_IND:
+ /* Ignore release confirm or release collision. */
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT)
+ break;
+ /* If MM fails or is rejected during U0.p state, this is a MM-EST-REJ. */
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING)
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_EST_REJ, &mmh->cause);
+ else
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_IND, &mmh->cause);
+ break;
+ case GSM48_MMGCC_DATA_IND:
+ case GSM48_MMBCC_DATA_IND:
+ rc = gsm44068_gcc_data_ind(trans, msg, mmh->transaction_id);
+ break;
+ case GSM48_MMGCC_NOTIF_IND:
+ case GSM48_MMBCC_NOTIF_IND:
+ switch (mmh->notify) {
+ case MMXX_NOTIFY_SETUP:
+ /* Store channel description. */
+ trans->gcc.ch_desc_present = mmh->ch_desc_present;
+ memcpy(&trans->gcc.ch_desc, &mmh->ch_desc, sizeof(trans->gcc.ch_desc));
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U0_NULL) {
+ /* In null state this indication is a SETUP-IND. */
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_IND, NULL);
+ } else if (trans->gcc.fi->state == VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE && mmh->ch_desc_present) {
+ /* In U2nc state with a channel info, is a SETUP-IND (with updated mode). */
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_IND, NULL);
+ }
+ break;
+ case MMXX_NOTIFY_RELEASE:
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE) {
+ /* If notification is gone, release pending received call. */
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_REL_IND, NULL);
+ }
+ break;
+ }
+ break;
+ case GSM48_MMGCC_GROUP_CNF:
+ case GSM48_MMBCC_GROUP_CNF:
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_JOIN_GC_CNF, NULL);
+ break;
+ case GSM48_MMGCC_UPLINK_CNF:
+ case GSM48_MMBCC_UPLINK_CNF:
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_CNF, NULL);
+ break;
+ case GSM48_MMGCC_UPLINK_REL_IND:
+ case GSM48_MMBCC_UPLINK_REL_IND:
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) {
+ /* We are waiting to send, so this is a reject. */
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REJ, &mmh->cause);
+ } else if (trans->gcc.fi->state == VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) {
+ /* We are waiting to receive, so this is a confirm. */
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_LISTEN_CNF, &mmh->cause);
+ }
+ break;
+ case GSM48_MMGCC_UPLINK_FREE_IND:
+ case GSM48_MMBCC_UPLINK_FREE_IND:
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_UPLINK_FREE, NULL);
+ break;
+ case GSM48_MMGCC_UPLINK_BUSY_IND:
+ case GSM48_MMBCC_UPLINK_BUSY_IND:
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_UPLINK_BUSY, NULL);
+ break;
+ default:
+ LOG_GCC(trans, LOGL_NOTICE, "Message '%s' unhandled.\n", get_mmxx_name(msg_type));
+ rc = -ENOTSUP;
+ }
+
+ return rc;
+}
+
+/* Special function to receive IDLE state of MM layer. This is required to request a channel after dedicated mode. */
+int gsm44068_rcv_mm_idle(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans;
+
+ trans = trans_find_ongoing_gcc_bcc(ms);
+ if (!trans)
+ return -ENOENT;
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE && trans->gcc.receive_after_sl) {
+ /* Finally the MM layer is IDLE. */
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_IDLE, NULL);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* Setup or join a VGC/VBC. */
+int gcc_bcc_call(struct osmocom_ms *ms, uint8_t protocol, const char *number)
+{
+ uint32_t callref = strtoul(number, NULL, 0);
+ struct gsm_trans *trans;
+ int i;
+
+ if (strlen(number) > 8) {
+inval:
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Invalid group '%s'\n", number);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < strlen(number); i++) {
+ if (number[i] < '0' || number[i] > '9')
+ goto inval;
+ }
+
+ if (callref == 0)
+ goto inval;
+
+ /* Reject if there is already an ongoing call. */
+ trans = trans_find_ongoing_gcc_bcc(ms);
+ if (trans) {
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Cannot call/join, we are busy\n");
+ return -EBUSY;
+ }
+
+ /* Find call that matches the given protocol+cellref. */
+ trans = trans_find_by_callref(ms, protocol, callref);
+ if (trans) {
+ /* Answer incoming call. */
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) {
+ osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_JOIN_GC_REQ, NULL);
+ return 0;
+ }
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call already established\n");
+ return -EEXIST;
+ }
+
+ /* Create new transaction. ORIG will be set when entering U2sl state. */
+ trans = trans_alloc_vgcs(ms, protocol, 0xff, callref);
+ if (!trans)
+ return -ENOMEM;
+
+ /* Setup new call. */
+ return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_REQ, NULL);
+}
+
+/* Leave a VGC. (If we are the originator, we do not terminate.) */
+int gcc_leave(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans;
+
+ /* Reject if there is no call. */
+ trans = trans_find_ongoing_gcc_bcc(ms);
+ if (!trans) {
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No Call\n");
+ return -EINVAL;
+ }
+
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U1_GROUP_CALL_INITIATED ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot leave (abort), in this state.\n");
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE)
+ vgcs_vty_notify(trans, "Cannot leave while talking\n");
+ return -EINVAL;
+ }
+
+ /* Send ABORT-REQ to leave the call. */
+ return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_REQ, NULL);
+}
+
+/* Hangup VGC/VBC. (If we are the originator, we terminate the call.) */
+int gcc_bcc_hangup(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans;
+
+ /* Reject if there is no call. */
+ trans = trans_find_ongoing_gcc_bcc(ms);
+ if (!trans) {
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No Call\n");
+ return -EINVAL;
+ }
+
+ if (trans->gcc.orig) {
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST ||
+ trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot hangup (terminate) in this state.\n");
+ return -EINVAL;
+ }
+ /* Send TERM-REQ to leave the call. */
+ return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TERM_REQ, NULL);
+ }
+
+ if (trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot hangup (abort) in this state.\n");
+ return -EINVAL;
+ }
+ /* Send ABORT-REQ to leave the call. */
+ return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_REQ, NULL);
+}
+
+int gcc_talk(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans;
+
+ /* Reject if there is no call. */
+ trans = trans_find_ongoing_gcc_bcc(ms);
+ if (!trans) {
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No Call\n");
+ return -EINVAL;
+ }
+
+ if (trans->protocol != GSM48_PDISC_GROUP_CC) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot talk, the ongoing call is not a group call.\n");
+ vgcs_vty_notify(trans, "Not a group call\n");
+ return -EIO;
+ }
+
+ if (trans->gcc.fi->state != VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot talk, we are not listening.\n");
+ return -EINVAL;
+ }
+
+ /* Send TALK-REQ to become talker. */
+ return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REQ, NULL);
+}
+
+int gcc_listen(struct osmocom_ms *ms)
+{
+ struct gsm_trans *trans;
+
+ /* Reject if there is no call. */
+ trans = trans_find_ongoing_gcc_bcc(ms);
+ if (!trans) {
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No Call\n");
+ return -EINVAL;
+ }
+
+ if (trans->protocol != GSM48_PDISC_GROUP_CC) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot become listener, the ongoing call is not a group call.\n");
+ vgcs_vty_notify(trans, "Not a group call\n");
+ return -EIO;
+ }
+
+ if (trans->gcc.fi->state != VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE &&
+ trans->gcc.fi->state != VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) {
+ LOG_GCC(trans, LOGL_NOTICE, "Cannot listen, we are not talking.\n");
+ return -EINVAL;
+ }
+
+ /* Send LISTEN-REQ to become listener. */
+ return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_LISTEN_REQ, NULL);
+}
+
+int gsm44068_dump_calls(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv)
+{
+ struct gsm_trans *trans;
+
+ llist_for_each_entry(trans, &ms->trans_list, entry) {
+ if (trans->protocol != GSM48_PDISC_GROUP_CC && trans->protocol != GSM48_PDISC_BCAST_CC)
+ continue;
+ print(priv, "Voice %s call '%d' is %s.\n",
+ (trans->protocol == GSM48_PDISC_GROUP_CC) ? "group" : "broadcast", trans->callref,
+ (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) ? "incoming" : "active");
+ }
+
+ return 0;
+}
diff --git a/src/host/layer23/src/mobile/gsm480_ss.c b/src/host/layer23/src/mobile/gsm480_ss.c
index 4ad2d7cb..0acd18d7 100644
--- a/src/host/layer23/src/mobile/gsm480_ss.c
+++ b/src/host/layer23/src/mobile/gsm480_ss.c
@@ -13,10 +13,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 <stdint.h>
@@ -26,15 +22,20 @@
#include <stdlib.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/gsm/protocol/gsm_04_80.h>
+#include <osmocom/gsm/gsm48.h>
+
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/transaction.h>
#include <osmocom/bb/mobile/gsm480_ss.h>
-#include <osmocom/core/talloc.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
#include <osmocom/bb/mobile/vty.h>
-#include <osmocom/gsm/protocol/gsm_04_80.h>
-#include <osmocom/gsm/gsm48.h>
static uint32_t new_callref = 0x80000001;
@@ -196,7 +197,7 @@ static const struct value_string Bearerservice_vals[] = {
static int gsm480_ss_result(struct osmocom_ms *ms, const char *response,
uint8_t error)
{
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
if (response) {
char text[256], *t = text, *s;
@@ -204,18 +205,18 @@ static int gsm480_ss_result(struct osmocom_ms *ms, const char *response,
while ((s = strchr(text, '\r')))
*s = '\n';
while ((s = strsep(&t, "\n"))) {
- vty_notify(ms, "Service response: %s\n", s);
+ l23_vty_ms_notify(ms, "Service response: %s\n", s);
}
} else if (error)
- vty_notify(ms, "Service request failed: %s\n",
+ l23_vty_ms_notify(ms, "Service request failed: %s\n",
get_value_string(gsm480_err_names, error));
else
- vty_notify(ms, "Service request failed.\n");
+ l23_vty_ms_notify(ms, "Service request failed.\n");
return 0;
}
-enum {
+enum gsm480_ss_state {
GSM480_SS_ST_IDLE = 0,
GSM480_SS_ST_REGISTER,
GSM480_SS_ST_ACTIVE,
@@ -263,8 +264,8 @@ void _gsm480_ss_trans_free(struct gsm_trans *trans)
msgb_free(trans->ss.msg);
trans->ss.msg = NULL;
}
- vty_notify(trans->ms, NULL);
- vty_notify(trans->ms, "Service connection terminated.\n");
+ l23_vty_ms_notify(trans->ms, NULL);
+ l23_vty_ms_notify(trans->ms, "Service connection terminated.\n");
}
/* release MM connection, free transaction */
@@ -286,6 +287,13 @@ static int gsm480_trans_free(struct gsm_trans *trans)
return 0;
}
+static void gsm480_trans_state_chg(struct gsm_trans *trans,
+ enum gsm480_ss_state state)
+{
+ trans->ss.state = state;
+ osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_STATE_CHG, trans);
+}
+
/*
* encoding
*/
@@ -364,7 +372,7 @@ static const char *ss_code_by_char(const char *code, uint8_t *ss_code)
static const char *decode_ss_code(uint8_t ss_code)
{
static char unknown[16];
-
+
switch (ss_code) {
case 33:
return "CFU";
@@ -602,6 +610,12 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans)
return -EIO;
}
+ /* ASCI call does not allow other transactions */
+ if (trans_find_ongoing_gcc_bcc(ms)) {
+ gsm480_ss_result(ms, "<ongoing ASCI call>", 0);
+ return -EBUSY;
+ }
+
/* allocate transaction with dummy reference */
transaction_id = trans_assign_trans_id(ms, GSM48_PDISC_NC_SS,
0);
@@ -620,8 +634,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans)
return -ENOMEM;
}
- /* go register sent state */
- trans->ss.state = GSM480_SS_ST_REGISTER;
+ gsm480_trans_state_chg(trans, GSM480_SS_ST_REGISTER);
/* FIXME: generate invoke ID */
trans->ss.invoke_id = 5;
@@ -646,7 +659,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans)
code++;
to = ss_code_by_char(code + 1, &ss_code);
-
+
/* register */
if (ss_code && to && to[0] == '*') {
OSMO_STRLCPY_ARRAY(dest, to + 1);
@@ -664,7 +677,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans)
uint8_t ss_code = 0;
ss_code_by_char(code + 2, &ss_code);
-
+
if (ss_code)
return gsm480_tx_cf(trans, GSM0480_MTYPE_REGISTER,
GSM0480_OP_CODE_ERASE_SS, ss_code, NULL);
@@ -674,7 +687,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans)
uint8_t ss_code = 0;
ss_code_by_char(code + 1, &ss_code);
-
+
if (ss_code)
return gsm480_tx_cf(trans, GSM0480_MTYPE_REGISTER,
GSM0480_OP_CODE_DEACTIVATE_SS, ss_code, NULL);
@@ -712,7 +725,7 @@ static int parse_tag_asn1(const uint8_t *data, int len,
/* check for buffer overflow */
if (len < *tag_len)
return -1;
-
+
/* return length */
return len;
}
@@ -789,7 +802,7 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
LOGP(DSS, LOGL_INFO, "call forwarding reply: len %d data %s\n", len,
osmo_hexdump(data, len));
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
/* forwarding feature list */
if (parse_tag_asn1(data, len, &tag_data, &tag_len) < 0) {
@@ -798,9 +811,9 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
}
if (data[0] == 0x80) {
if ((tag_data[0] & 0x01))
- vty_notify(ms, "Status: activated\n");
+ l23_vty_ms_notify(ms, "Status: activated\n");
else
- vty_notify(ms, "Status: deactivated\n");
+ l23_vty_ms_notify(ms, "Status: deactivated\n");
return 0;
}
@@ -819,7 +832,7 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
/* check for SS code */
if (data[0] != 0x04)
break;
- vty_notify(ms, "Reply for %s\n", decode_ss_code(tag_data[0]));
+ l23_vty_ms_notify(ms, "Reply for %s\n", decode_ss_code(tag_data[0]));
len -= tag_data - data + tag_len;
data = tag_data + tag_len;
/* sequence tag */
@@ -835,10 +848,10 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
data = tag_data;
break;
default:
- vty_notify(ms, "Call Forwarding reply unsupported.\n");
+ l23_vty_ms_notify(ms, "Call Forwarding reply unsupported.\n");
return 0;
}
-
+
while (len) {
/* sequence tag */
if (parse_tag_asn1(data, len, &tag_data, &tag_len) < 0) {
@@ -867,20 +880,20 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
osmo_hexdump(tag_data, tag_len));
switch (data2[0]) {
case 0x82:
- vty_notify(ms, "Bearer Service: %s\n",
+ l23_vty_ms_notify(ms, "Bearer Service: %s\n",
get_value_string(Bearerservice_vals,
tag_data[0]));
break;
case 0x83:
- vty_notify(ms, "Teleservice: %s\n",
+ l23_vty_ms_notify(ms, "Teleservice: %s\n",
get_value_string(Teleservice_vals,
tag_data[0]));
break;
case 0x84:
if ((tag_data[0] & 0x01))
- vty_notify(ms, "Status: activated\n");
+ l23_vty_ms_notify(ms, "Status: activated\n");
else
- vty_notify(ms, "Status: deactivated\n");
+ l23_vty_ms_notify(ms, "Status: deactivated\n");
break;
case 0x85:
if (((tag_data[0] & 0x70) >> 4) == 1)
@@ -892,7 +905,7 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
gsm48_decode_bcd_number2(number + strlen(number),
sizeof(number) - strlen(number),
tag_data - 1, tag_len + 1, 1);
- vty_notify(ms, "Destination: %s\n", number);
+ l23_vty_ms_notify(ms, "Destination: %s\n", number);
break;
}
len2 -= tag_data - data2 + tag_len;
@@ -1058,7 +1071,12 @@ static int gsm480_rx_release_comp(struct gsm_trans *trans, struct msgb *msg)
struct tlv_parsed tp;
int rc = 0;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DSS, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ gsm480_trans_free(trans);
+ return -EINVAL;
+ }
+
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
rc = gsm480_rx_fac_ie(trans, TLVP_VAL(&tp, GSM48_IE_FACILITY),
*(TLVP_VAL(&tp, GSM48_IE_FACILITY)-1));
@@ -1091,11 +1109,15 @@ static int gsm480_rx_facility(struct gsm_trans *trans, struct msgb *msg)
struct tlv_parsed tp;
int rc = 0;
- /* go register state */
- trans->ss.state = GSM480_SS_ST_ACTIVE;
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_FACILITY, 0) < 0) {
+ LOGP(DSS, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ /* XXX: indicate an error somehow */
+ return -EINVAL;
+ }
+
+ gsm480_trans_state_chg(trans, GSM480_SS_ST_ACTIVE);
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_FACILITY, 0);
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
rc = gsm480_rx_fac_ie(trans, TLVP_VAL(&tp, GSM48_IE_FACILITY),
*(TLVP_VAL(&tp, GSM48_IE_FACILITY)-1));
@@ -1123,10 +1145,14 @@ static int gsm480_rx_register(struct gsm_trans *trans, struct msgb *msg)
struct tlv_parsed tp;
int rc = 0;
- /* go register state */
- trans->ss.state = GSM480_SS_ST_ACTIVE;
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DSS, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ /* XXX: indicate an error somehow */
+ return -EINVAL;
+ }
+
+ gsm480_trans_state_chg(trans, GSM480_SS_ST_ACTIVE);
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
rc = gsm480_rx_fac_ie(trans, TLVP_VAL(&tp, GSM48_IE_FACILITY),
*(TLVP_VAL(&tp, GSM48_IE_FACILITY)-1));
@@ -1243,7 +1269,7 @@ int gsm480_rcv_ss(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_trans *trans;
int rc = 0;
- trans = trans_find_by_callref(ms, mmh->ref);
+ trans = trans_find_by_callref(ms, GSM48_PDISC_NC_SS, mmh->ref);
if (!trans) {
LOGP(DSS, LOGL_INFO, " -> (new transaction)\n");
trans = trans_alloc(ms, GSM48_PDISC_NC_SS, mmh->transaction_id,
diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c
index de0d0352..94b81cb0 100644
--- a/src/host/layer23/src/mobile/gsm48_cc.c
+++ b/src/host/layer23/src/mobile/gsm48_cc.c
@@ -13,10 +13,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 <stdint.h>
@@ -29,13 +25,16 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/transaction.h>
#include <osmocom/bb/mobile/gsm48_cc.h>
-#include <osmocom/bb/mobile/voice.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
+#include <osmocom/bb/mobile/tch.h>
#include <l1ctl_proto.h>
static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
@@ -44,6 +43,8 @@ int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans,
uint32_t callref, int location, int value);
static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg);
+static void gsm48_cc_trans_bcap_update(struct gsm_trans *trans,
+ const struct gsm_mncc *mncc);
/*
* init
@@ -55,7 +56,7 @@ int gsm48_cc_init(struct osmocom_ms *ms)
cc->ms = ms;
- if (!cc->mncc_upqueue.next == 0)
+ if (cc->mncc_upqueue.next != NULL)
return 0;
LOGP(DCC, LOGL_INFO, "init Call Control\n");
@@ -183,11 +184,13 @@ static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans,
/* enqueue message to application (MNCC-SAP) */
static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans,
- int msg_type, struct gsm_mncc *mncc)
+ uint32_t msg_type, struct gsm_mncc *mncc)
{
struct gsm48_cclayer *cc = &ms->cclayer;
struct msgb *msg;
+ gsm48_cc_trans_bcap_update(trans, mncc);
+
if (trans)
LOGP(DCC, LOGL_INFO, "(ms %s ti %x) Sending '%s' to MNCC.\n",
ms->name, trans->transaction_id,
@@ -241,6 +244,8 @@ static void new_cc_state(struct gsm_trans *trans, int state)
gsm48_cc_state_name(state));
trans->cc.state = state;
+
+ osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_STATE_CHG, trans);
}
/*
@@ -369,6 +374,9 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans)
{
gsm48_stop_cc_timer(trans);
+ talloc_free(trans->cc.bcap);
+ trans->cc.bcap = NULL;
+
/* disable audio distribution */
if (trans->ms->mncc_entity.ref == trans->callref)
trans->ms->mncc_entity.ref = 0;
@@ -384,6 +392,16 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans)
new_cc_state(trans, GSM_CSTATE_NULL);
}
+static void gsm48_cc_trans_bcap_update(struct gsm_trans *trans,
+ const struct gsm_mncc *mncc)
+{
+ if (~mncc->fields & MNCC_F_BEARER_CAP)
+ return;
+ if (trans->cc.bcap == NULL)
+ trans->cc.bcap = talloc(trans, struct gsm_mncc_bearer_cap);
+ memcpy(trans->cc.bcap, &mncc->bearer_cap, sizeof(mncc->bearer_cap));
+}
+
/* release MM connection, go NULL state, free transaction */
static int gsm48_rel_null_free(struct gsm_trans *trans)
{
@@ -625,10 +643,14 @@ static int gsm48_cc_rx_progress(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received PROGRESS\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_PROGR_IND, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&progress, 0, sizeof(struct gsm_mncc));
progress.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_PROGR_IND, 0);
/* progress */
if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
progress.fields |= MNCC_F_PROGRESS;
@@ -656,13 +678,17 @@ static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans,
struct tlv_parsed tp;
struct gsm_mncc call_proc;
- LOGP(DCC, LOGL_INFO, "sending CALL PROCEEDING\n");
+ LOGP(DCC, LOGL_INFO, "received CALL PROCEEDING\n");
+
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
gsm48_stop_cc_timer(trans);
memset(&call_proc, 0, sizeof(struct gsm_mncc));
call_proc.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
#if 0
/* repeat */
if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
@@ -714,12 +740,16 @@ static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received ALERTING\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
/* no T301 in MS call control */
memset(&alerting, 0, sizeof(struct gsm_mncc));
alerting.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* facility */
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
alerting.fields |= MNCC_F_FACILITY;
@@ -755,11 +785,15 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received CONNECT\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&connect, 0, sizeof(struct gsm_mncc));
connect.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* facility */
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
connect.fields |= MNCC_F_FACILITY;
@@ -825,10 +859,13 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received SETUP\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&setup, 0, sizeof(struct gsm_mncc));
setup.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
-
/* bearer capability */
if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
setup.fields |= MNCC_F_BEARER_CAP;
@@ -1078,9 +1115,13 @@ static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received START DTMF ACKNOWLEDGE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&dtmf, 0, sizeof(struct gsm_mncc));
dtmf.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* keypad facility */
if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) {
dtmf.fields |= MNCC_F_KEYPAD;
@@ -1141,9 +1182,13 @@ static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received STOP DTMF ACKNOWLEDGE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&dtmf, 0, sizeof(struct gsm_mncc));
dtmf.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf);
}
@@ -1337,15 +1382,18 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received USERINFO\n");
- memset(&user, 0, sizeof(struct gsm_mncc));
- user.callref = trans->callref;
if (payload_len < 1) {
- LOGP(DCC, LOGL_NOTICE, "Short read of userinfo message "
- "error.\n");
+ LOGP(DCC, LOGL_NOTICE, "Short read of USERINFO message\n");
return -EINVAL;
}
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_USER_USER, 0);
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_USER_USER, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&user, 0, sizeof(struct gsm_mncc));
+ user.callref = trans->callref;
/* user-user */
gsm48_decode_useruser(&user.useruser,
TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
@@ -1419,17 +1467,20 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received MODIFY REJECT\n");
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of MODIFY REJECT message\n");
+ return -EINVAL;
+ }
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&modify, 0, sizeof(struct gsm_mncc));
modify.callref = trans->callref;
- if (payload_len < 1) {
- LOGP(DCC, LOGL_NOTICE, "Short read of modify reject message "
- "error.\n");
- return -EINVAL;
- }
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
/* bearer capability */
if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
modify.fields |= MNCC_F_BEARER_CAP;
@@ -1677,14 +1728,18 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received DISCONNECT\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_CAUSE, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND);
memset(&disc, 0, sizeof(struct gsm_mncc));
disc.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_CAUSE, 0);
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
disc.fields |= MNCC_F_CAUSE;
@@ -1726,11 +1781,15 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received RELEASE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&rel, 0, sizeof(struct gsm_mncc));
rel.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
rel.fields |= MNCC_F_CAUSE;
@@ -1795,11 +1854,15 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received RELEASE COMPLETE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&rel, 0, sizeof(struct gsm_mncc));
rel.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
rel.fields |= MNCC_F_CAUSE;
@@ -1937,10 +2000,16 @@ int mncc_tx_to_cc(void *inst, int msg_type, void *arg)
return -EBUSY;
}
+ /* ASCI call does not allow other transactions */
+ if (trans_find_ongoing_gcc_bcc(ms)) {
+ LOGP(DCC, LOGL_NOTICE, "Phone is busy doing ASCI call\n");
+ return -EBUSY;
+ }
+
data->msg_type = msg_type;
/* Find callref */
- trans = trans_find_by_callref(ms, data->callref);
+ trans = trans_find_by_callref(ms, GSM48_PDISC_CC, data->callref);
if (!trans) {
/* check for SETUP message */
@@ -1968,9 +2037,15 @@ int mncc_tx_to_cc(void *inst, int msg_type, void *arg)
}
}
+ gsm48_cc_trans_bcap_update(trans, data);
+
switch (msg_type) {
case GSM_TCHF_FRAME:
- return gsm_send_voice(ms, arg);
+ case GSM_TCHF_FRAME_EFR:
+ case GSM_TCHH_FRAME:
+ case GSM_TCH_FRAME_AMR:
+ case GSM_BAD_FRAME:
+ return tch_send_mncc_frame(ms, arg);
case MNCC_LCHAN_MODIFY:
return 0;
case MNCC_FRAME_RECV:
@@ -2095,15 +2170,21 @@ static struct datastate {
static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg)
{
struct osmocom_ms *ms = trans->ms;
- struct gsm48_hdr *gh = msgb_l3(msg);
- int msg_type = gh->msg_type & 0xbf;
- uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
- /* flip */
+ const struct gsm48_hdr *gh = msgb_l3(msg);
int msg_supported = 0; /* determine, if message is supported at all */
+ uint8_t msg_type;
int i, rc;
- /* set transaction ID, if not already */
- trans->transaction_id = transaction_id;
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOGP(DCC, LOGL_INFO, "%s(): short read of msgb: %s\n",
+ __func__, msgb_hexdump(msg));
+ return -EINVAL;
+ }
+
+ msg_type = gh->msg_type & 0xbf;
+
+ /* set transaction ID (flip), if not already */
+ trans->transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
/* pull the MMCC header */
msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
@@ -2146,7 +2227,7 @@ int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_trans *trans;
int rc = 0;
- trans = trans_find_by_callref(ms, mmh->ref);
+ trans = trans_find_by_callref(ms, GSM48_PDISC_CC, mmh->ref);
if (!trans) {
trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id,
mmh->ref);
diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c
index 49cc2bc6..fad475e2 100644
--- a/src/host/layer23/src/mobile/gsm48_mm.c
+++ b/src/host/layer23/src/mobile/gsm48_mm.c
@@ -13,10 +13,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 <stdint.h>
@@ -30,18 +26,24 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/common/subscriber.h>
#include <osmocom/bb/mobile/gsm48_cc.h>
#include <osmocom/bb/mobile/gsm480_ss.h>
#include <osmocom/bb/mobile/gsm411_sms.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
#include <osmocom/bb/mobile/app_mobile.h>
#include <osmocom/bb/mobile/primitives.h>
#include <osmocom/bb/mobile/vty.h>
-#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
+#include <osmocom/bb/mobile/gsm322.h>
extern void *l23_ctx;
@@ -59,6 +61,9 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate);
static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_group_reject(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_uplink_reject(struct osmocom_ms *ms, struct msgb *msg);
/*
* notes
@@ -143,6 +148,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* During LIMITED SERVICE state: (4.2.2.3)
* - reject MM connection except for emergency calls
* - perform location update, if new LAI is entered
+ * - indicate GCC/BCC calls with channel description only
+ * - reject joining to GCC/BCC calls without channel description
+ * - accept joining to GCC/BCC calls with channel description
*
*
* The LOCATION UPDATE NEEDED state is entered if:
@@ -175,6 +183,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* - accept MM connection for emergency calls
* - trigger location update on any other MM connection
* - respond to paging (with IMSI only, because in U2 TMSI is not valid)
+ * - indicate GCC/BCC calls with channel description only
+ * - reject joining to GCC/BCC calls without channel description
+ * - accept joining to GCC/BCC calls with channel description
*
*
* The NORMAL SERVICE state is entered if:
@@ -184,12 +195,52 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* - and SIM LAI == cell
*
* During NORMAL SERVICE state: (4.2.2.1)
- * - on expirery of T3211 or T3213: Perform location updated
- * - on expirery of T3212: Perform location updated
+ * - on expirery of T3211 or T3213: Perform location update
+ * - on expirery of T3212: Perform location update
* - on change of LAI: Perform location update
* - perform IMSI detach
* - perform MM connections
* - respond to paging
+ * - indicate GCC/BCC calls with and without channel description
+ * - accept joining to GCC/BCC calls without channel description
+ * -> The GCC/BCC layer waits for channel description before joining.
+ * - accept joining to GCC/BCC calls with channel description
+ *
+ *
+ * The RECEIVING GROUP CALL (NORMAL SERVICE) is entered if:
+ * - the upper layer requests to join to GCC/BCC call
+ * - and service state is NORMAL SERVICE
+ *
+ * During RECEIVING GROUP CALL (NORMAL SERVICE) state: (4.2.2.7)
+ * - reject all MM connections
+ * - indicate notifications and paging to GCC or BCC layer
+ * -> If supported by RR layer.
+ * The following events are not be supported here:
+ * - perform IMSI detach (This is delayed until the call has ended.)
+ * - on expirery of T3211 or T3213: Perform location update
+ * - on expirery of T3212: Perform location update
+ * - accept MM connections
+ * - on change of LAI: Perform location update
+ * - accept joining to GCC/BCC calls without channel description
+ * - accept joining to GCC/BCC calls with channel description
+ *
+ *
+ * The RECEIVING GROUP CALL (LIMITED SERVICE) is entered if:
+ * - the upper layer requests to join to GCC/BCC call
+ * - and service state is LIMITED SERVICE or ATTEMPTING TO UPDATE
+ *
+ * During RECEIVING GROUP CALL (LIMITED SERVICE) state: (4.2.2.8)
+ * - reject all MM connections
+ * - indicate notifications and paging to GCC or BCC layer
+ * -> If supported by RR layer.
+ * The following events are not be supported here:
+ * - reject MM connection except for emergency calls
+ * - on expirery of T3212: Perform location updated
+ * - reject joining to GCC/BCC calls without channel description
+ * - accept joining to GCC/BCC calls with channel description
+ *
+ *
+ * A group call is only accepted, if there is no other MM connection ongoing.
*
*
* gsm48_mm_set_plmn_search() is used enter PLMN SEARCH or PLMN SEARCH NORMAL
@@ -222,6 +273,33 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
*
* gsm48_mm_cell_selected() is used to select the Service state.
*
+ *
+ * New primitives are invented for group/broadcast calls. They are not
+ * specified in any recommendation. They are:
+ *
+ * - MMxCC_NOTIF_IND: The MM layer indicates new/updated/ceased calls. This is
+ * completely independent from the state of the MM layer.
+ * - MMxCC_GROUP_REQ: The GCC/BCC layer requests group channel in receive mode.
+ * This mode has no MM connection. Speical flags (mm->vgcs*) are used to
+ * define that mode and store the reference (callref + group|broadcast). This
+ * reference is used for messages towards GCC/BCC layer. The state is IDLE
+ * and the sub-state defines group receive mode at normal service or at
+ * limited service state.
+ * - MMxCC_GROUP_CNF: The MM layer confirms group channel.
+ * - MMxCC_UPLINK_REQ: The GCC/BCC layer requests uplink (group transmit mode).
+ * The MM layer changes to group transmit mode.
+ * - MMxCC_UPLINK_CNF: The MM layer confirms uplink. (Uplink was granted.)
+ * - MMxCC_UPLINK_REL_REQ: The GCC/BCC layer requests release of uplink.
+ * - MMxCC_UPLINK_REL_IND: The MM layer indicates/confirms release of uplink
+ *
+ * Existing primitives are used with group calls:
+ *
+ * MMxCC_REL_IND: Failed to establish group receive mode.
+ * MMxCC_ERR_IND: Abort received from RR layer in group receive or transmit mode.
+ * MMxCC_REL_REQ: Leave group call (receive mode or transmit mode).
+ *
+ * The group call is released at MM layer, if one of the primitives above are
+ * received or transmitted.
*/
/*
@@ -271,46 +349,68 @@ static int decode_network_name(char *name, int name_len,
return length;
}
-/* encode 'mobile identity' */
-int gsm48_encode_mi(uint8_t *buf, struct msgb *msg, struct osmocom_ms *ms,
- uint8_t mi_type)
+/* Encode and append 'mobile identity' of given type to message, based on current settings. */
+int gsm48_encode_mi_lv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi)
{
struct gsm_subscriber *subscr = &ms->subscr;
struct gsm_settings *set = &ms->settings;
- uint8_t *ie;
+ struct osmo_mobile_identity mi = { };
+ int rc;
+ uint8_t *l;
- switch(mi_type) {
+ /* Copy MI values according to their types. */
+ switch (mi_type) {
case GSM_MI_TYPE_TMSI:
- gsm48_generate_mid_from_tmsi(buf, subscr->tmsi);
+ mi.tmsi = subscr->tmsi;
break;
case GSM_MI_TYPE_IMSI:
- gsm48_generate_mid_from_imsi(buf, subscr->imsi);
+ if (emergency_imsi)
+ OSMO_STRLCPY_ARRAY(mi.imsi, set->emergency_imsi);
+ else
+ OSMO_STRLCPY_ARRAY(mi.imsi, subscr->imsi);
break;
case GSM_MI_TYPE_IMEI:
- gsm48_generate_mid_from_imsi(buf, set->imei);
+ OSMO_STRLCPY_ARRAY(mi.imei, set->imei);
break;
case GSM_MI_TYPE_IMEISV:
- gsm48_generate_mid_from_imsi(buf, set->imeisv);
- break;
- case GSM_MI_TYPE_NONE:
- default:
- buf[0] = GSM48_IE_MOBILE_ID;
- buf[1] = 1;
- buf[2] = 0xf0;
+ OSMO_STRLCPY_ARRAY(mi.imeisv, set->imeisv);
break;
}
- /* alter MI type */
- buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | mi_type;
- if (msg) {
- /* MI as LV */
- ie = msgb_put(msg, 1 + buf[1]);
- memcpy(ie, buf + 1, 1 + buf[1]);
+ /* Generate MI or 'NONE', if not available. */
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ case GSM_MI_TYPE_IMSI:
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ mi.type = mi_type;
+ l = msgb_put(msg, 1);
+ rc = osmo_mobile_identity_encode_msgb(msg, &mi, true);
+ if (rc < 0) {
+ LOGP(DMM, LOGL_ERROR, "Failed to encode mobile identity type %d. Please fix!\n", mi_type);
+ *l = 1;
+ msgb_put_u8(msg, 0xf0 | GSM_MI_TYPE_NONE);
+ break;
+ }
+ *l = rc;
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, 0xf0 | mi_type);
}
return 0;
}
+int gsm48_encode_mi_tlv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi)
+{
+ /* Append IE type. */
+ msgb_put_u8(msg, GSM48_IE_MOBILE_ID);
+
+ return gsm48_encode_mi_lv(ms, msg, mi_type, emergency_imsi);
+}
+
/* encode 'classmark 1' */
int gsm48_encode_classmark1(struct gsm48_classmark1 *cm, uint8_t rev_lev,
uint8_t es_ind, uint8_t a5_1, uint8_t pwr_lev)
@@ -554,6 +654,9 @@ static const struct value_string gsm48_mmevent_names[] = {
{ GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" },
{ GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" },
{ GSM48_MM_EVENT_LOST_COVERAGE, "MM_EVENT_LOST_COVERAGE" },
+ { GSM48_MM_EVENT_NOTIFICATION, "MM_EVENT_NOTIFICATION" },
+ { GSM48_MM_EVENT_UPLINK_FREE, "MM_EVENT_UPLINK_FREE" },
+ { GSM48_MM_EVENT_UPLINK_BUSY, "MM_EVENT_UPLINK_BUSY" },
{ 0, NULL }
};
@@ -638,6 +741,46 @@ static const struct value_string gsm48_mmxx_msg_names[] = {
{ GSM48_MMSMS_ERR_IND, "MMSMS_ERR_IND" },
{ GSM48_MMSMS_PROMPT_IND, "MMSMS_PROMPT_IND" },
{ GSM48_MMSMS_PROMPT_REJ, "MMSMS_PROMPT_REJ" },
+ { GSM48_MMGCC_EST_REQ, "MMGCC_EST_REQ" },
+ { GSM48_MMGCC_EST_CNF, "MMGCC_EST_CNF" },
+ { GSM48_MMGCC_REL_REQ, "MMGCC_REL_REQ" },
+ { GSM48_MMGCC_REL_IND, "MMGCC_REL_IND" },
+ { GSM48_MMGCC_DATA_REQ, "MMGCC_DATA_REQ" },
+ { GSM48_MMGCC_DATA_IND, "MMGCC_DATA_IND" },
+ { GSM48_MMGCC_UNIT_DATA_REQ, "MMGCC_UNIT_DATA_REQ" },
+ { GSM48_MMGCC_UNIT_DATA_IND, "MMGCC_UNIT_DATA_IND" },
+ { GSM48_MMGCC_REEST_REQ, "MMBCC_REEST_REQ" },
+ { GSM48_MMGCC_REEST_CNF, "MMBCC_REEST_CNF" },
+ { GSM48_MMGCC_ERR_IND, "MMGCC_ERR_IND" },
+ { GSM48_MMGCC_NOTIF_IND, "MMGCC_NOTIF_IND" },
+ { GSM48_MMGCC_GROUP_REQ, "MMGCC_GROUP_REQ" },
+ { GSM48_MMGCC_GROUP_CNF, "MMGCC_GROUP_CNF" },
+ { GSM48_MMGCC_UPLINK_REQ, "MMGCC_UPLINK_REQ" },
+ { GSM48_MMGCC_UPLINK_CNF, "MMGCC_UPLINK_CNF" },
+ { GSM48_MMGCC_UPLINK_REL_REQ, "MMGCC_UPLINK_REL_REQ" },
+ { GSM48_MMGCC_UPLINK_REL_IND, "MMGCC_UPLINK_REL_CNF" },
+ { GSM48_MMGCC_UPLINK_FREE_IND, "MMGCC_UPLINK_FREE_IND" },
+ { GSM48_MMGCC_UPLINK_BUSY_IND, "MMGCC_UPLINK_BUSY_IND" },
+ { GSM48_MMBCC_EST_REQ, "MMBCC_EST_REQ" },
+ { GSM48_MMBCC_EST_CNF, "MMBCC_EST_CNF" },
+ { GSM48_MMBCC_REL_REQ, "MMBCC_REL_REQ" },
+ { GSM48_MMBCC_REL_IND, "MMBCC_REL_IND" },
+ { GSM48_MMBCC_DATA_REQ, "MMBCC_DATA_REQ" },
+ { GSM48_MMBCC_DATA_IND, "MMBCC_DATA_IND" },
+ { GSM48_MMBCC_UNIT_DATA_REQ, "MMBCC_UNIT_DATA_REQ" },
+ { GSM48_MMBCC_UNIT_DATA_IND, "MMBCC_UNIT_DATA_IND" },
+ { GSM48_MMBCC_REEST_REQ, "MMBCC_REEST_REQ" },
+ { GSM48_MMBCC_REEST_CNF, "MMBCC_REEST_CNF" },
+ { GSM48_MMBCC_ERR_IND, "MMBCC_ERR_IND" },
+ { GSM48_MMBCC_NOTIF_IND, "MMBCC_NOTIF_IND" },
+ { GSM48_MMBCC_GROUP_REQ, "MMBCC_GROUP_REQ" },
+ { GSM48_MMBCC_GROUP_CNF, "MMBCC_GROUP_CNF" },
+ { GSM48_MMBCC_UPLINK_REQ, "MMBCC_UPLINK_REQ" },
+ { GSM48_MMBCC_UPLINK_CNF, "MMBCC_UPLINK_CNF" },
+ { GSM48_MMBCC_UPLINK_REL_REQ, "MMBCC_UPLINK_REL_REQ" },
+ { GSM48_MMBCC_UPLINK_REL_IND, "MMBCC_UPLINK_REL_CNF" },
+ { GSM48_MMBCC_UPLINK_FREE_IND, "MMBCC_UPLINK_FREE_IND" },
+ { GSM48_MMBCC_UPLINK_BUSY_IND, "MMBCC_UPLINK_BUSY_IND" },
{ 0, NULL }
};
@@ -763,6 +906,10 @@ int gsm48_mmxx_dequeue(struct osmocom_ms *ms)
case GSM48_MMSMS_CLASS:
gsm411_rcv_sms(ms, msg);
break;
+ case GSM48_MMGCC_CLASS:
+ case GSM48_MMBCC_CLASS:
+ gsm44068_rcv_gcc_bcc(ms, msg);
+ break;
}
msgb_free(msg);
work = 1; /* work done */
@@ -864,8 +1011,8 @@ const char *gsm48_mm_state_names[] = {
"wait for RR connection active",
"MM idle",
"wait for additional outgoing MM connection",
- "MM_CONN_ACTIVE_VGCS",
- "WAIT_RR_CONN_VGCS",
+ "MM connection active (group transmit mode)",
+ "wait for RR connection (group transmit mode)",
"location updating pending",
"IMSI detach pending",
"RR connection release not allowed"
@@ -881,8 +1028,8 @@ const char *gsm48_mm_substate_names[] = {
"location updating needed",
"PLMN search",
"PLMN search (normal)",
- "RX_VGCS_NORMAL",
- "RX_VGCS_LIMITED"
+ "receiving group call (normal service)",
+ "receiving group call (limiteed service)"
};
/* change state from LOCATION UPDATE NEEDED to ATTEMPTING TO UPDATE */
@@ -925,34 +1072,39 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
&& (mm->state != GSM48_MM_ST_MM_IDLE || mm->substate != substate)) {
switch (substate) {
case GSM48_MM_SST_NORMAL_SERVICE:
- vty_notify(ms, NULL);
- vty_notify(ms, "On Network, normal service: %s, %s\n",
- gsm_get_mcc(plmn->mcc),
- gsm_get_mnc(plmn->mcc, plmn->mnc));
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "On Network, normal service: %s, %s\n",
+ gsm_get_mcc(plmn->plmn.mcc),
+ gsm_get_mnc(&plmn->plmn));
break;
case GSM48_MM_SST_LIMITED_SERVICE:
- vty_notify(ms, NULL);
- vty_notify(ms, "Limited service, emergency calls are "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Limited service, emergency calls are "
"possible.\n");
break;
case GSM48_MM_SST_PLMN_SEARCH_NORMAL:
case GSM48_MM_SST_PLMN_SEARCH:
- vty_notify(ms, NULL);
- vty_notify(ms, "Searching network...\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Searching network...\n");
break;
case GSM48_MM_SST_NO_IMSI:
- vty_notify(ms, NULL);
- vty_notify(ms, "No SIM, emergency calls are "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No SIM, emergency calls are "
"possible.\n");
break;
case GSM48_MM_SST_NO_CELL_AVAIL:
- vty_notify(ms, NULL);
- vty_notify(ms, "No service.\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No service.\n");
break;
case GSM48_MM_SST_ATTEMPT_UPDATE:
- vty_notify(ms, NULL);
- vty_notify(ms, "Trying to registering with "
- "network...\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Trying to register with network %s, %s...\n",
+ gsm_get_mcc(plmn->plmn.mcc), gsm_get_mnc(&plmn->plmn));
+ break;
+ case GSM48_MM_SST_RX_VGCS_NORMAL:
+ case GSM48_MM_SST_RX_VGCS_LIMITED:
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Listening to %s call.\n", (mm->vgcs.group_call) ? "group" : "broadcast");
break;
}
}
@@ -975,6 +1127,8 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
if (!nmsg)
return;
gsm48_mmevent_msg(ms, nmsg);
+ /* Stop here and wait for the IMSI_DETACH event being handled. */
+ return;
}
/* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */
@@ -1021,6 +1175,15 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
/* must exit, because this function can be called recursively */
return;
}
+
+ /* Tell the group call that we are ready for a new channel activation. */
+ if (state == GSM48_MM_ST_MM_IDLE
+ && (substate == GSM48_MM_SST_NORMAL_SERVICE
+ || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) {
+ gsm44068_rcv_mm_idle(ms);
+ /* must exit, because this function can be called recursively */
+ return;
+ }
}
/* return PLMN SEARCH or PLMN SEARCH NORMAL state */
@@ -1042,7 +1205,7 @@ static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms)
"SIM not updated.\n");
return GSM48_MM_SST_PLMN_SEARCH;
}
- if (subscr->lac == 0x0000 || subscr->lac >= 0xfffe) {
+ if (subscr->lai.lac == 0x0000 || subscr->lai.lac >= 0xfffe) {
LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
"LAI in SIM not valid.\n");
return GSM48_MM_SST_PLMN_SEARCH;
@@ -1056,15 +1219,12 @@ static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms)
}
/* selected cell's LAI not equal to LAI stored on the sim */
- if (cs->sel_mcc != subscr->mcc
- || cs->sel_mnc != subscr->mnc
- || cs->sel_lac != subscr->lac) {
+ if (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) != 0) {
LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
- "LAI of selected cell (MCC %s MNC %s LAC 0x%04x) "
- "!= LAI in SIM (MCC %s MNC %s LAC 0x%04x).\n",
- gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc),
- cs->sel_lac, gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac);
+ "LAI of selected cell (MCC-MNC %s LAC 0x%04x) "
+ "!= LAI in SIM (MCC-MNC %s LAC 0x%04x).\n",
+ osmo_plmn_name(&cs->sel_cgi.lai.plmn), cs->sel_cgi.lai.lac,
+ osmo_plmn_name2(&subscr->lai.plmn), subscr->lai.lac);
return GSM48_MM_SST_PLMN_SEARCH;
}
@@ -1081,6 +1241,14 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm48_sysinfo *s = &cs->sel_si;
+ /* When we are in group transmit mode, we return to group receive mode. */
+ if (mm->vgcs.enabled) {
+ LOGP(DMM, LOGL_INFO, "Return to group receive mode.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL
+ : GSM48_MM_SST_RX_VGCS_LIMITED);
+ return 0;
+ }
+
if (cs->state != GSM322_C3_CAMPED_NORMALLY
&& cs->state != GSM322_C7_CAMPED_ANY_CELL) {
LOGP(DMM, LOGL_INFO, "Not camping, wait for CS process to "
@@ -1120,10 +1288,8 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
/* if we are attached and selected cell equals the registered LAI */
if (subscr->imsi_attached
- && subscr->lac /* valid */
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac) {
+ && subscr->lai.lac /* valid */
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)) {
LOGP(DMM, LOGL_INFO, "We are in registered LAI as returning "
"to MM IDLE\n");
/* if SIM not updated (abnormal case as described in 4.4.4.9) */
@@ -1131,8 +1297,7 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
GSM48_MM_SST_ATTEMPT_UPDATE);
else
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
return 0;
}
@@ -1140,27 +1305,22 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
if (cs->state == GSM322_C3_CAMPED_NORMALLY) {
LOGP(DMM, LOGL_INFO, "We are camping normally as returning to "
"MM IDLE\n");
- if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc,
- cs->sel_mnc)) {
+ if (gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)) {
/* location update not allowed */
LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
} else
- if (gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc,
- cs->sel_lac)) {
+ if (gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai)) {
/* location update not allowed */
LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
} else
/* 4.4.4.9 if cell is barred, don't start */
if ((!subscr->acc_barr && s->cell_barr)
|| (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) &
(s->class_barr ^ 0xffff)))) {
LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
} else {
/* location update allowed */
LOGP(DMM, LOGL_INFO, "Loc. upd. allowed.\n");
@@ -1171,8 +1331,7 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
/* location update not allowed */
LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning "
"to MM IDLE\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
}
return 0;
@@ -1200,6 +1359,13 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_sysinfo *s = &cs->sel_si;
struct gsm_settings *set = &ms->settings;
+ if (mm->vgcs.enabled) {
+ LOGP(DMM, LOGL_ERROR, "Cell selection in group receive mode, this should not happen.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL
+ : GSM48_MM_SST_RX_VGCS_LIMITED);
+ return 0;
+ }
+
/* no SIM is inserted */
if (!subscr->sim_valid) {
LOGP(DMM, LOGL_INFO, "SIM invalid as cell is selected.\n");
@@ -1210,17 +1376,14 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
/* SIM not updated in this LA */
if (subscr->ustate == GSM_SIM_U1_UPDATED
- && subscr->lac /* valid */
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac
+ && subscr->lai.lac /* valid */
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)
&& !mm->lupd_periodic) {
if (subscr->imsi_attached) {
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Valid in location area.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
@@ -1234,8 +1397,7 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Attachment not required.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
@@ -1251,14 +1413,12 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
/* PLMN mode auto and selected cell is forbidden */
if (set->plmn_mode == PLMN_MODE_AUTO
&& (!cs->selected
- || gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)
- || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc,
- cs->sel_lac))) {
+ || gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)
+ || gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai))) {
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
@@ -1273,14 +1433,12 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
* in M3 state the PLMN is not selected for registration. */
if (set->plmn_mode == PLMN_MODE_MANUAL
&& (!cs->selected
- || plmn->mcc != cs->sel_mcc
- || plmn->mnc != cs->sel_mnc
+ || (osmo_plmn_cmp(&plmn->plmn, &cs->sel_cgi.lai.plmn) != 0)
|| plmn->state == GSM322_M3_NOT_ON_PLMN)) {
struct msgb *nmsg;
LOGP(DMM, LOGL_INFO, "Selected cell not found.\n");
- new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_LIMITED_SERVICE);
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE);
/* send message to PLMN search process */
nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
@@ -1388,9 +1546,8 @@ uint32_t mm_conn_new_ref = 0x80000001;
/* new MM connection state */
static void new_conn_state(struct gsm48_mm_conn *conn, int state)
{
- LOGP(DMM, LOGL_INFO, "(ref %x) new state %s -> %s\n", conn->ref,
- gsm48_mmxx_state_names[conn->state],
- gsm48_mmxx_state_names[state]);
+ LOGP(DMM, LOGL_INFO, "(ref 0x%x proto %d) new state %s -> %s\n", conn->ref, conn->protocol,
+ gsm48_mmxx_state_names[conn->state], gsm48_mmxx_state_names[state]);
conn->state = state;
}
@@ -1409,13 +1566,35 @@ struct gsm48_mm_conn *mm_conn_by_id(struct gsm48_mmlayer *mm,
}
/* find MM connection by reference */
-struct gsm48_mm_conn *mm_conn_by_ref(struct gsm48_mmlayer *mm,
- uint32_t ref)
+struct gsm48_mm_conn *mm_conn_by_ref_and_class(struct gsm48_mmlayer *mm,
+ uint32_t ref, uint16_t cls)
{
struct gsm48_mm_conn *conn;
+ uint8_t protocol;
+
+ switch (cls) {
+ case GSM48_MMCC_CLASS:
+ protocol = GSM48_PDISC_CC;
+ break;
+ case GSM48_MMSS_CLASS:
+ protocol = GSM48_PDISC_NC_SS;
+ break;
+ case GSM48_MMSMS_CLASS:
+ protocol = GSM48_PDISC_SMS;
+ break;
+ case GSM48_MMGCC_CLASS:
+ protocol = GSM48_PDISC_GROUP_CC;
+ break;
+ case GSM48_MMBCC_CLASS:
+ protocol = GSM48_PDISC_BCAST_CC;
+ break;
+ default:
+ LOGP(DMM, LOGL_ERROR, "Invalid message class 0x%03x. Please fix!", cls);
+ return NULL;
+ }
llist_for_each_entry(conn, &mm->mm_conn, list) {
- if (conn->ref == ref)
+ if (conn->ref == ref && conn->protocol == protocol)
return conn;
}
return NULL;
@@ -1465,6 +1644,7 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any,
struct gsm48_mm_conn *conn, *conn2;
struct msgb *nmsg;
struct gsm48_mmxx_hdr *nmmh;
+ int msg_type;
/* Note: For SAPI 0 all connections are released */
@@ -1480,35 +1660,44 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any,
/* abort any OR the pending connection */
if ((abort_any || conn->state == GSM48_MMXX_ST_CONN_PEND)
&& (sapi == conn->sapi || sapi == 0)) {
- /* send MMxx-REL-IND */
- nmsg = NULL;
+ /* send MMXX-REL-IND or MMXX-ERR-IND */
switch(conn->protocol) {
case GSM48_PDISC_CC:
- nmsg = gsm48_mmxx_msgb_alloc(
- error ? GSM48_MMCC_ERR_IND
- : GSM48_MMCC_REL_IND, conn->ref,
- conn->transaction_id,
- conn->sapi);
+ msg_type = (error) ? GSM48_MMCC_ERR_IND
+ : GSM48_MMCC_REL_IND;
break;
case GSM48_PDISC_NC_SS:
- nmsg = gsm48_mmxx_msgb_alloc(
- error ? GSM48_MMSS_ERR_IND
- : GSM48_MMSS_REL_IND, conn->ref,
- conn->transaction_id,
- conn->sapi);
+ msg_type = (error) ? GSM48_MMSS_ERR_IND
+ : GSM48_MMSS_REL_IND;
break;
case GSM48_PDISC_SMS:
- nmsg = gsm48_mmxx_msgb_alloc(
- error ? GSM48_MMSMS_ERR_IND
- : GSM48_MMSMS_REL_IND, conn->ref,
- conn->transaction_id,
- conn->sapi);
+ msg_type = (error) ? GSM48_MMSMS_ERR_IND
+ : GSM48_MMSMS_REL_IND;
+ break;
+ case GSM48_PDISC_GROUP_CC:
+ msg_type = (error) ? GSM48_MMGCC_ERR_IND
+ : GSM48_MMGCC_REL_IND;
break;
+ case GSM48_PDISC_BCAST_CC:
+ msg_type = (error) ? GSM48_MMBCC_ERR_IND
+ : GSM48_MMBCC_REL_IND;
+ break;
+ default:
+ msg_type = -1;
}
- if (!nmsg) {
+ if (msg_type == -1) {
/* this should not happen */
+ LOGP(DMM, LOGL_ERROR, "MM connection of "
+ "unsupported protocol? Please fix!\n");
+ mm_conn_free(conn);
+ continue;
+ }
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, conn->ref,
+ conn->transaction_id,
+ conn->sapi);
+ if (!nmsg) {
mm_conn_free(conn);
- continue; /* skip if not of CC type */
+ continue;
}
nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
nmmh->cause = cause;
@@ -1584,7 +1773,7 @@ static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
/* LAI */
- gsm48_decode_lai_hex(lai, &subscr->mcc, &subscr->mnc, &subscr->lac);
+ gsm48_decode_lai2(lai, &subscr->lai);
/* MI */
mi = gh->data + sizeof(struct gsm48_loc_area_id);
mi_type = mi[1] & GSM_MI_TYPE_MASK;
@@ -1600,12 +1789,12 @@ static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg)
gsm48_mm_tx_tmsi_reall_cpl(ms);
break;
case GSM_MI_TYPE_IMSI:
- subscr->tmsi = 0xffffffff;
+ subscr->tmsi = GSM_RESERVED_TMSI;
LOGP(DMM, LOGL_INFO, "TMSI removed.\n");
gsm48_mm_tx_tmsi_reall_cpl(ms);
break;
default:
- subscr->tmsi = 0xffffffff;
+ subscr->tmsi = GSM_RESERVED_TMSI;
LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown MI "
"type %d.\n", mi_type);
gsm48_mm_tx_mm_status(ms, GSM48_REJECT_INCORRECT_MESSAGE);
@@ -1697,8 +1886,8 @@ static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg)
subscr->sim_valid = 0;
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -1754,7 +1943,7 @@ static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_mm_tx_mm_status(ms,
GSM48_REJECT_MSG_NOT_COMPATIBLE);
}
- if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == 0xffffffff) {
+ if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == GSM_RESERVED_TMSI) {
LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST of TMSI, but we have no "
"TMSI\n");
return gsm48_mm_tx_mm_status(ms,
@@ -1769,7 +1958,6 @@ static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type)
{
struct msgb *nmsg;
struct gsm48_hdr *ngh;
- uint8_t buf[11];
LOGP(DMM, LOGL_INFO, "IDENTITY RESPONSE\n");
@@ -1781,8 +1969,8 @@ static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type)
ngh->proto_discr = GSM48_PDISC_MM;
ngh->msg_type = GSM48_MT_MM_ID_RESP;
- /* MI */
- gsm48_encode_mi(buf, nmsg, ms, mi_type);
+ /* MI (LV) */
+ gsm48_encode_mi_lv(ms, nmsg, mi_type, false);
/* push RR header and send down */
return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0, 0);
@@ -1800,7 +1988,6 @@ static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim)
struct msgb *nmsg;
struct gsm48_hdr *ngh;
uint8_t pwr_lev;
- uint8_t buf[11];
struct gsm48_classmark1 cm;
@@ -1821,13 +2008,13 @@ static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim)
pwr_lev = gsm48_current_pwr_lev(set, rr->cd_now.arfcn);
gsm48_encode_classmark1(&cm, sup->rev_lev, sup->es_ind, set->a5_1,
pwr_lev);
- msgb_v_put(nmsg, *((uint8_t *)&cm));
- /* MI */
- if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */
- gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_TMSI);
+ msgb_v_put(nmsg, *((uint8_t *)&cm));
+ /* MI (LV) */
+ if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi);
} else {
- gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_IMSI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi);
}
@@ -1962,6 +2149,34 @@ static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_mm_imsi_detach_sent(ms, msg);
}
+/* Detach during VGCS. Queue and return idle. */
+static int gsm48_mm_imsi_detach_vgcs(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+ int msg_type;
+
+ LOGP(DMM, LOGL_INFO, "IMSI detach delayed until group receive/transmit mode is left.\n");
+
+ /* remember to detach later */
+ mm->delay_detach = 1;
+
+ /* Release group call. */
+ gsm48_mm_group_rel_req(ms, msg);
+
+ /* Release message to GCC/BCC layer */
+ msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND;
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
/* ignore ongoing IMSI detach */
static int gsm48_mm_imsi_detach_ignore(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -2016,8 +2231,8 @@ static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg)
subscr->sim_valid = 0;
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2044,11 +2259,13 @@ static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg)
struct tlv_parsed tp;
if (payload_len < 0) {
- LOGP(DMM, LOGL_NOTICE, "Short read of MM INFORMATION message "
- "error.\n");
+ LOGP(DMM, LOGL_ERROR, "Short read of MM INFORMATION message\n");
+ return -EINVAL;
+ }
+ if (tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DMM, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
return -EINVAL;
}
- tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0);
/* long name */
if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) {
@@ -2078,6 +2295,8 @@ static int gsm48_mm_sysinfo(struct osmocom_ms *ms, struct msgb *msg)
if (mm->state == GSM48_MM_ST_MM_IDLE
&& (mm->substate == GSM48_MM_SST_NO_CELL_AVAIL
|| mm->substate == GSM48_MM_SST_LIMITED_SERVICE
+ || mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL
+ || mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED
|| mm->substate == GSM48_MM_SST_PLMN_SEARCH
|| mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL))
return 0;
@@ -2169,20 +2388,18 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
"forbidden PLMN.\n");
LOGP(DSUM, LOGL_INFO, "Location updating is disabled by "
"configuration\n");
- gsm_subscr_add_forbidden_plmn(subscr, cs->sel_mcc,
- cs->sel_mnc, GSM48_REJECT_PLMN_NOT_ALLOWED);
+ gsm_subscr_add_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn, GSM48_REJECT_PLMN_NOT_ALLOWED);
msg_type = GSM322_EVENT_REG_FAILED;
goto _stop;
}
/* if LAI is forbidden, don't start */
- if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) {
+ if (gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)) {
LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n");
msg_type = GSM322_EVENT_REG_FAILED;
goto stop;
}
- if (gsm322_is_forbidden_la(ms, cs->sel_mcc,
- cs->sel_mnc, cs->sel_lac)) {
+ if (gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai)) {
LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n");
msg_type = GSM322_EVENT_REG_FAILED;
goto stop;
@@ -2197,13 +2414,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
goto stop;
}
- mm->lupd_mcc = cs->sel_mcc;
- mm->lupd_mnc = cs->sel_mnc;
- mm->lupd_lac = cs->sel_lac;
+ memcpy(&mm->lupd_lai, &cs->sel_cgi.lai, sizeof(mm->lupd_lai));
- LOGP(DSUM, LOGL_INFO, "Perform location update (MCC %s, MNC %s "
- "LAC 0x%04x)\n", gsm_print_mcc(mm->lupd_mcc),
- gsm_print_mnc(mm->lupd_mnc), mm->lupd_lac);
+ LOGP(DSUM, LOGL_INFO, "Perform location update (LAI=%s)\n", osmo_lai_name(&mm->lupd_lai));
return gsm48_mm_tx_loc_upd_req(ms);
}
@@ -2215,6 +2428,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_subscriber *subscr = &ms->subscr;
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm48_sysinfo *s = &cs->sel_si;
+ bool vgcs = mm->vgcs.enabled;
struct msgb *nmsg;
/* in case we already have a location update going on */
@@ -2246,17 +2460,16 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
/* if location update is not required */
if (subscr->ustate == GSM_SIM_U1_UPDATED
&& cs->selected
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)
&& (subscr->imsi_attached
|| !s->att_allowed)) {
LOGP(DMM, LOGL_INFO, "Loc. upd. not required.\n");
- subscr->imsi_attached = 1;
+ subscr->imsi_attached = true;
/* go straight to normal service state */
new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
- GSM48_MM_SST_NORMAL_SERVICE);
+ (vgcs) ? GSM48_MM_SST_RX_VGCS_NORMAL
+ : GSM48_MM_SST_NORMAL_SERVICE);
#if 0
/* don't send message, if we got not triggered by PLMN search */
@@ -2276,9 +2489,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
/* 4.4.3 is attachment required? */
if (subscr->ustate == GSM_SIM_U1_UPDATED
&& cs->selected
- && cs->sel_mcc == subscr->mcc
- && cs->sel_mnc == subscr->mnc
- && cs->sel_lac == subscr->lac
+ && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)
&& !subscr->imsi_attached
&& s->att_allowed) {
/* do location update for IMSI attach */
@@ -2335,7 +2546,6 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
struct gsm48_hdr *ngh;
struct gsm48_loc_upd_req *nlu; /* NOTE: mi_len is part of struct */
uint8_t pwr_lev;
- uint8_t buf[11];
LOGP(DMM, LOGL_INFO, "LOCATION UPDATING REQUEST\n");
@@ -2343,7 +2553,8 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
if (!nmsg)
return -ENOMEM;
ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
- nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu));
+ /* Do not add mi_len to the message, this is done at gsm48_encode_mi_lv(). */
+ nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu) - 1);
ngh->proto_discr = GSM48_PDISC_MM;
ngh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST;
@@ -2356,24 +2567,20 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
*
* NOTE: The TMSI is only valid within a LAI!
*/
- gsm48_encode_lai_hex(&nlu->lai, subscr->mcc, subscr->mnc, subscr->lac);
- LOGP(DMM, LOGL_INFO, " using LAI (mcc %s mnc %s " "lac 0x%04x)\n",
- gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac);
+ gsm48_generate_lai2(&nlu->lai, &subscr->lai);
+ LOGP(DMM, LOGL_INFO, " using LAI=%s\n", osmo_lai_name(&subscr->lai));
/* classmark 1 */
pwr_lev = gsm48_current_pwr_lev(set, cs->sel_arfcn);
gsm48_encode_classmark1(&nlu->classmark1, sup->rev_lev, sup->es_ind,
set->a5_1, pwr_lev);
- /* MI */
- if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI);
+ /* MI (LV) */
+ if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi);
} else {
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi);
}
- msgb_put(nmsg, buf[1]); /* length is part of nlu */
- memcpy(&nlu->mi_len, buf + 1, 1 + buf[1]);
new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_LUPD, 0);
@@ -2406,15 +2613,17 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
struct tlv_parsed tp;
struct msgb *nmsg;
- if (payload_len < sizeof(struct gsm48_loc_area_id)) {
- short_read:
- LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING ACCEPT "
- "message error.\n");
+ if (payload_len < sizeof(*lai)) {
+short_read:
+ LOGP(DMM, LOGL_ERROR, "Short read of LOCATION UPDATING ACCEPT message\n");
+ return -EINVAL;
+ }
+ if (tlv_parse(&tp, &gsm48_mm_att_tlvdef,
+ gh->data + sizeof(*lai),
+ payload_len - sizeof(*lai), 0, 0) < 0) {
+ LOGP(DMM, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
return -EINVAL;
}
- tlv_parse(&tp, &gsm48_mm_att_tlvdef,
- gh->data + sizeof(struct gsm48_loc_area_id),
- payload_len - sizeof(struct gsm48_loc_area_id), 0, 0);
/* update has finished */
mm->lupd_pending = 0;
@@ -2426,7 +2635,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
stop_mm_t3212(mm); /* 4.4.2 */
/* LAI */
- gsm48_decode_lai_hex(lai, &subscr->mcc, &subscr->mnc, &subscr->lac);
+ gsm48_decode_lai2(lai, &subscr->lai);
/* stop location update timer */
stop_mm_t3210(mm);
@@ -2435,7 +2644,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
mm->lupd_attempt = 0;
/* mark SIM as attached */
- subscr->imsi_attached = 1;
+ subscr->imsi_attached = true;
/* set the status in the sim to updated */
new_sim_ustate(subscr, GSM_SIM_U1_UPDATED);
@@ -2444,19 +2653,17 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
gsm_subscr_write_loci(ms);
/* set last registered PLMN */
- if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) {
+ if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) {
subscr->plmn_valid = 1;
- subscr->plmn_mcc = subscr->mcc;
- subscr->plmn_mnc = subscr->mnc;
+ memcpy(&subscr->plmn, &subscr->lai.plmn, sizeof(struct osmo_plmn_id));
}
LOGP(DSUM, LOGL_INFO, "Location update accepted\n");
- LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (mcc %s mnc %s "
- "lac 0x%04x)\n", gsm_print_mcc(subscr->mcc),
- gsm_print_mnc(subscr->mnc), subscr->lac);
+ LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (lai=%s)\n",
+ osmo_lai_name(&subscr->lai));
/* remove LA from forbidden list */
- gsm322_del_forbidden_la(ms, subscr->mcc, subscr->mnc, subscr->lac);
+ gsm322_del_forbidden_la(ms, &subscr->lai);
/* MI */
if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
@@ -2486,7 +2693,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
break;
case GSM_MI_TYPE_IMSI:
LOGP(DMM, LOGL_INFO, "TMSI removed\n");
- subscr->tmsi = 0xffffffff;
+ subscr->tmsi = GSM_RESERVED_TMSI;
/* store LOCI on sim */
gsm_subscr_write_loci(ms);
@@ -2507,7 +2714,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
gsm322_plmn_sendmsg(ms, nmsg);
/* follow on proceed */
- if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID))
+ if (TLVP_PRESENT(&tp, GSM48_IE_FOLLOW_ON_PROC))
LOGP(DMM, LOGL_NOTICE, "follow-on proceed not supported.\n");
/* start RR release timer */
@@ -2581,8 +2788,8 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
case GSM48_REJECT_LOC_NOT_ALLOWED:
case GSM48_REJECT_ROAMING_NOT_ALLOWED:
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2631,15 +2838,13 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal ME)\n");
break;
case GSM48_REJECT_PLMN_NOT_ALLOWED:
- gsm_subscr_add_forbidden_plmn(subscr, mm->lupd_mcc,
- mm->lupd_mnc, mm->lupd_rej_cause);
+ gsm_subscr_add_forbidden_plmn(subscr, &mm->lupd_lai.plmn, mm->lupd_rej_cause);
LOGP(DSUM, LOGL_INFO, "Location update failed (PLMN not "
"allowed)\n");
break;
case GSM48_REJECT_LOC_NOT_ALLOWED:
case GSM48_REJECT_ROAMING_NOT_ALLOWED:
- gsm322_add_forbidden_la(ms, mm->lupd_mcc, mm->lupd_mnc,
- mm->lupd_lac, mm->lupd_rej_cause);
+ gsm322_add_forbidden_la(ms, &mm->lupd_lai, mm->lupd_rej_cause);
LOGP(DSUM, LOGL_INFO, "Location update failed (LAI not "
"allowed)\n");
break;
@@ -2686,9 +2891,7 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg)
stop_mm_t3210(mm);
if (subscr->ustate == GSM_SIM_U1_UPDATED
- && mm->lupd_mcc == subscr->mcc
- && mm->lupd_mnc == subscr->mnc
- && mm->lupd_lac == subscr->lac) {
+ && (osmo_lai_cmp(&mm->lupd_lai, &subscr->lai) == 0)) {
if (mm->lupd_attempt < 4) {
LOGP(DSUM, LOGL_INFO, "Try location update later\n");
LOGP(DMM, LOGL_INFO, "Loc. upd. failed, retry #%d\n",
@@ -2704,8 +2907,8 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg)
}
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2797,7 +3000,6 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
struct gsm48_hdr *ngh;
struct gsm48_service_request *nsr; /* NOTE: includes MI length */
uint8_t *cm2lv;
- uint8_t buf[11];
LOGP(DMM, LOGL_INFO, "CM SERVICE REQUEST (cause %d)\n", mm->est_cause);
@@ -2805,7 +3007,8 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
if (!nmsg)
return -ENOMEM;
ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
- nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr));
+ /* Do not add mi_len to the message, this is done at gsm48_encode_mi_lv(). */
+ nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr) - 1);
cm2lv = (uint8_t *)&nsr->classmark;
ngh->proto_discr = GSM48_PDISC_MM;
@@ -2823,23 +3026,21 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
if (mm->est_cause == RR_EST_CAUSE_EMERGENCY && set->emergency_imsi[0]) {
LOGP(DMM, LOGL_INFO, "-> Using IMSI %s for emergency\n",
set->emergency_imsi);
- gsm48_generate_mid_from_imsi(buf, set->emergency_imsi);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, true);
} else
if (!subscr->sim_valid) { /* have no SIM ? */
LOGP(DMM, LOGL_INFO, "-> Using IMEI %s\n",
set->imei);
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMEI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMEI, false);
} else
- if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI);
+ if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DMM, LOGL_INFO, "-> Using TMSI\n");
} else {
- gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DMM, LOGL_INFO, "-> Using IMSI %s\n",
subscr->imsi);
}
- msgb_put(nmsg, buf[1]); /* length is part of nsr */
- memcpy(&nsr->mi_len, buf + 1, 1 + buf[1]);
/* prio is optional for eMLPP */
/* push RR header and send down */
@@ -2912,8 +3113,8 @@ static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
abort_any = 1;
/* TMSI and LAI invalid */
- subscr->tmsi = 0xffffffff;
- subscr->lac = 0x0000;
+ subscr->tmsi = GSM_RESERVED_TMSI;
+ subscr->lai.lac = 0x0000;
/* key is invalid */
subscr->key_seq = 7;
@@ -2989,21 +3190,10 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg,
*/
sapi = conn_found->sapi;
reject:
- nmsg = NULL;
- switch(msg_type) {
- case GSM48_MMCC_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND,
- mmh->ref, mmh->transaction_id, sapi);
- break;
- case GSM48_MMSS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND,
- mmh->ref, mmh->transaction_id, sapi);
- break;
- case GSM48_MMSMS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND,
- mmh->ref, mmh->transaction_id, sapi);
- break;
- }
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMXX_REL_IND |
+ (msg_type & GSM48_MMXX_MASK),
+ mmh->ref, mmh->transaction_id,
+ sapi);
if (!nmsg)
return -ENOMEM;
nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
@@ -3077,6 +3267,16 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg,
cm_serv = GSM48_CMSERV_SMS;
proto = GSM48_PDISC_SMS;
break;
+ case GSM48_MMGCC_EST_REQ:
+ cause = RR_EST_CAUSE_OTHER_SDCCH;
+ cm_serv = GSM48_CMSERV_VGCS;
+ proto = GSM48_PDISC_GROUP_CC;
+ break;
+ case GSM48_MMBCC_EST_REQ:
+ cause = RR_EST_CAUSE_OTHER_SDCCH;
+ cm_serv = GSM48_CMSERV_VBS;
+ proto = GSM48_PDISC_BCAST_CC;
+ break;
}
/* create MM connection instance */
@@ -3212,21 +3412,10 @@ static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *nmmh;
/* reject */
- nmsg = NULL;
- switch(msg_type) {
- case GSM48_MMCC_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref,
- mmh->transaction_id, sapi);
- break;
- case GSM48_MMSS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref,
- mmh->transaction_id, sapi);
- break;
- case GSM48_MMSMS_EST_REQ:
- nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref,
- mmh->transaction_id, sapi);
- break;
- }
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMXX_REL_IND |
+ (msg_type & GSM48_MMXX_MASK),
+ mmh->ref, mmh->transaction_id,
+ sapi);
if (!nmsg)
return -ENOMEM;
nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
@@ -3292,6 +3481,16 @@ static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms)
conn_found->ref, conn_found->transaction_id,
conn_found->sapi);
break;
+ case GSM48_PDISC_GROUP_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMGCC_EST_CNF,
+ conn_found->ref, conn_found->transaction_id,
+ conn_found->sapi);
+ break;
+ case GSM48_PDISC_BCAST_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMBCC_EST_CNF,
+ conn_found->ref, conn_found->transaction_id,
+ conn_found->sapi);
+ break;
}
if (!nmsg)
return -ENOMEM;
@@ -3358,6 +3557,7 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
+ uint32_t msg_type = rrh->msg_type;
int cause;
/* stop RR release timer */
@@ -3377,11 +3577,13 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg)
cause = 47;
}
+ LOGP(DMM, LOGL_INFO, "Aborting connection with cause %d\n", cause);
+
/* stop MM connection timer */
stop_mm_t3230(mm);
/* release all connections */
- gsm48_mm_release_mm_conn(ms, 1, cause, 1, 0);
+ gsm48_mm_release_mm_conn(ms, 1, cause, (msg_type == GSM48_RR_ABORT_IND), 0);
/* return to MM IDLE */
return gsm48_mm_return_idle(ms, NULL);
@@ -3425,37 +3627,32 @@ static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
struct gsm48_mm_conn *conn;
int msg_type = mmh->msg_type;
+ uint8_t sapi;
- /* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
- if (!conn) {
- LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already "
- "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref);
- switch(msg_type & GSM48_MMXX_MASK) {
- case GSM48_MMCC_CLASS:
- mmh->msg_type = GSM48_MMCC_REL_IND;
- break;
- case GSM48_MMSS_CLASS:
- mmh->msg_type = GSM48_MMSS_REL_IND;
- break;
- case GSM48_MMSMS_CLASS:
- mmh->msg_type = GSM48_MMSMS_REL_IND;
- break;
- }
- mmh->cause = 31;
+ if (mm->state == GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) {
+ /* Group transmit mode has no MM connection. */
+ sapi = 0;
+ } else {
+ /* get connection, if not exist (anymore), release */
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
+ if (!conn) {
+ LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already "
+ "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref);
+ mmh->msg_type = GSM48_MMXX_REL_IND | (msg_type & GSM48_MMXX_MASK);
+ mmh->cause = 31;
- /* mirror message with REL_IND + cause */
- return gsm48_mmxx_upmsg(ms, msg);
+ /* mirror message with REL_IND + cause */
+ return gsm48_mmxx_upmsg(ms, msg);
+ }
+ /* set SAPI, if upper layer does not do it correctly */
+ sapi = conn->sapi;
}
- /* set SAPI, if upper layer does not do it correctly */
- mmh->sapi = conn->sapi;
-
/* pull MM header */
msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
/* push RR header and send down */
- return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, conn->sapi, 0);
+ return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, sapi, 0);
}
/* release of MM connection (active state) */
@@ -3466,7 +3663,7 @@ static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3490,7 +3687,7 @@ static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3505,7 +3702,7 @@ static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3534,7 +3731,7 @@ static int gsm48_mm_release_wait_rr(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_mm_conn *conn;
/* get connection, if not exist (anymore), release */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
mm_conn_free(conn);
@@ -3562,6 +3759,351 @@ static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_mm_return_idle(ms, NULL);
}
+/* The RR indicates notification of ongoing VGCS/VBS calls. */
+static int gsm48_mm_notification(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mm_event *mme = (struct gsm48_mm_event *)msg->data;
+ uint32_t ref = osmo_load32be(mme->notification.gcr) >> 5;
+ uint16_t msg_type = (mme->notification.gcr[3] & 0x10) ? GSM48_MMGCC_NOTIF_IND : GSM48_MMBCC_NOTIF_IND;
+ struct gsm48_mmxx_hdr *mmh;
+ struct msgb *nmsg;
+
+ /* Notification message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, ref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+ mmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ mmh->notify = (mme->notification.gone) ? MMXX_NOTIFY_RELEASE : MMXX_NOTIFY_SETUP;
+ mmh->ch_desc_present = mme->notification.ch_desc_present;
+ memcpy(&mmh->ch_desc, &mme->notification.ch_desc, sizeof(mmh->ch_desc));
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* The RR indicates uplink busy. */
+static int gsm48_mm_uplink_free(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mm_event *mme = (struct gsm48_mm_event *)msg->data;
+ struct msgb *nmsg;
+ uint16_t msg_type;
+
+ if (mm->vgcs.group_call)
+ msg_type = (mme->msg_type == GSM48_MM_EVENT_UPLINK_BUSY) ? GSM48_MMGCC_UPLINK_BUSY_IND
+ : GSM48_MMGCC_UPLINK_FREE_IND;
+ else
+ msg_type = (mme->msg_type == GSM48_MM_EVENT_UPLINK_BUSY) ? GSM48_MMBCC_UPLINK_BUSY_IND
+ : GSM48_MMBCC_UPLINK_FREE_IND;
+
+ LOGP(DMM, LOGL_INFO, "Update uplink free/busy state in group receive mode.\n");
+
+ /* Notification message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* Join VGCS/VBS call as listener. */
+static int gsm48_mm_group_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_settings *set = &ms->settings;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct msgb *nmsg;
+
+ if (mm->substate == GSM48_MM_SST_NO_IMSI && !set->asci_allow_any)
+ return gsm48_mm_group_reject(ms, msg);
+
+ LOGP(DMM, LOGL_INFO, "Request for joining a group call, trying to establish group receive mode.\n");
+
+ /* Store infos about group/broadcast call. */
+ mm->vgcs.enabled = true;
+ mm->vgcs.group_call = (mmh->msg_type == GSM48_MMGCC_GROUP_REQ);
+ mm->vgcs.callref = mmh->ref;
+ mm->vgcs.normal_service = (mm->substate == GSM48_MM_SST_NORMAL_SERVICE ||
+ mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL);
+
+ /* Change to VGCS substate. */
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service)
+ ? GSM48_MM_SST_RX_VGCS_NORMAL : GSM48_MM_SST_RX_VGCS_LIMITED);
+
+ /* Group recevie mode request to RR layer */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+
+ /* Add channel description. */
+ memcpy(msgb_put(nmsg, sizeof(mmh->ch_desc)), &mmh->ch_desc, sizeof(mmh->ch_desc));
+
+ /* Push RR header and send to RR layer. */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REQ, 0, 0);
+}
+
+/* Joining VGCS/VBS call is not allowed in other states. */
+static int gsm48_mm_group_reject(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ uint16_t msg_type;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ LOGP(DMM, LOGL_NOTICE, "Joining group call rejected in current state.\n");
+
+ msg_type = (mmh->msg_type == GSM48_MMGCC_GROUP_REQ) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND;
+
+ /* Release message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, mmh->sapi);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED;
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* RR layer confirms group call. */
+static int gsm48_mm_group_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ uint16_t msg_type;
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_NOTICE, "RR confirms group call.\n");
+
+ msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_GROUP_CNF : GSM48_MMBCC_GROUP_CNF;
+
+ /* Uplink confirm message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* RR layer releases group call channel. */
+static int gsm48_mm_group_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
+ uint16_t msg_type;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ LOGP(DMM, LOGL_NOTICE, "RR released or rejected group call channel.\n");
+
+ /* Disable group mode. */
+ mm->vgcs.enabled = false;
+
+ /* Change mode back to normal or limited service. */
+ if (mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED) {
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (subscr->sim_valid) ? GSM48_MM_SST_LIMITED_SERVICE
+ : GSM48_MM_SST_NO_IMSI);
+ }
+ if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL)
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
+
+ /* Return IDLE, if not already. Also select the sub-state to use. */
+ gsm48_mm_return_idle(ms, NULL);
+
+ /* Release message to GCC/BCC layer */
+ msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND;
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ switch (rrh->cause) {
+ case RR_REL_CAUSE_TRY_LATER:
+ /* Joining not yet possible */
+ nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE;
+ break;
+ case RR_REL_CAUSE_LOST_SIGNAL:
+ /* Lower layer failed. */
+ nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE;
+ break;
+ case RR_REL_CAUSE_NORMAL:
+ /* Channel was released by network. */
+ nmmh->cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
+ break;
+ default:
+ nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
+ break;
+ }
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* Upper layer releases group call. */
+static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "Request to release group call in receive or transmit mode.\n");
+
+ /* Disable group mode. */
+ mm->vgcs.enabled = false;
+
+ /* Change mode back to normal or limited service. */
+ if (mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED) {
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (subscr->sim_valid) ? GSM48_MM_SST_LIMITED_SERVICE
+ : GSM48_MM_SST_NO_IMSI);
+ }
+ if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL)
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE);
+
+ /* We are already IDLE. Also select the sub-state to use. */
+ gsm48_mm_return_idle(ms, NULL);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+
+ /* Push RR header and send to RR layer. */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REL_REQ, 0, 0);
+}
+
+/* Upper layer requests uplink. */
+static int gsm48_mm_uplink_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_settings *set = &ms->settings;
+ struct msgb *nmsg;
+
+ if (mm->substate != GSM48_MM_SST_RX_VGCS_NORMAL && !set->asci_allow_any)
+ return gsm48_mm_uplink_reject(ms, msg);
+
+ LOGP(DMM, LOGL_INFO, "Request for uplink, trying to establish group transmit mode.\n");
+
+ /* Go into uplink pending state. */
+ new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_VGCS, 0);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+
+ /* Push RR header and send to RR layer. */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_UPLINK_REQ, 0, 0);
+}
+
+/* Uplink not allowed in this state. */
+static int gsm48_mm_uplink_reject(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ uint16_t msg_type;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ LOGP(DMM, LOGL_NOTICE, "Request for uplink rejected in current state.\n");
+
+ msg_type = (mmh->msg_type == GSM48_MMGCC_UPLINK_REQ) ? GSM48_MMGCC_UPLINK_REL_IND : GSM48_MMBCC_UPLINK_REL_IND;
+
+ /* Uplink release message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, mmh->sapi);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED;
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* RR layer confirms uplink. */
+static int gsm48_mm_uplink_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ uint16_t msg_type;
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_NOTICE, "RR confirms uplink.\n");
+
+ /* Go into group transmit state. */
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE_VGCS, 0);
+
+ msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_UPLINK_CNF : GSM48_MMBCC_UPLINK_CNF;
+
+ /* Uplink confirm message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* RR layer releases/rejects uplink. */
+static int gsm48_mm_uplink_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
+ uint16_t msg_type;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ LOGP(DMM, LOGL_NOTICE, "RR released or rejected uplink.\n");
+
+ /* Change to VGCS substate. */
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL
+ : GSM48_MM_SST_RX_VGCS_LIMITED);
+
+ msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_UPLINK_REL_IND : GSM48_MMBCC_UPLINK_REL_IND;
+
+ /* Uplink reject message to GCC/BCC layer */
+ nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ switch (rrh->cause) {
+ case RR_REL_CAUSE_UPLINK_REJECTED:
+ /* Access to uplink was rejected by network or when not in group receive mode. */
+ nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED;
+ break;
+ case RR_REL_CAUSE_UPLINK_BUSY:
+ /* Uplink was busy and did not become free. */
+ nmmh->cause = GSM48_CC_CAUSE_USER_BUSY;
+ break;
+ case RR_REL_CAUSE_LINK_FAILURE:
+ /* Access to uplink failed. */
+ nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE;
+ break;
+ case RR_REL_CAUSE_NORMAL:
+ /* Uplink was released by message. */
+ nmmh->cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
+ break;
+ default:
+ nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
+ break;
+ }
+
+ gsm48_mmxx_upmsg(ms, nmsg);
+ return 0;
+}
+
+/* Upper layer releases uplink. */
+static int gsm48_mm_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "Request to release uplink, leaving group transmit mode.\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+
+ /* Push RR header and send to RR layer. */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_UPLINK_REL_REQ, 0, 0);
+}
+
/*
* other processes
*/
@@ -3640,19 +4182,51 @@ static struct downstate {
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.2 Attempt to update / Loc. Upd. needed */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, /* emergency only */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.3 Limited service */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.4 No IMSI */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.5 PLMN search, normal service */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
@@ -3663,10 +4237,54 @@ static struct downstate {
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
/* 4.2.2.6 PLMN search */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req},
+
+ /* 4.2.2.7 Receiving group call, normal service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL),
+ GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL),
+ GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL),
+ GSM48_MMGCC_UPLINK_REQ, gsm48_mm_uplink_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL),
+ GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_req},
+
+ /* 4.2.2.8 Receiving group call, limited service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MMGCC_UPLINK_REQ, gsm48_mm_uplink_req},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_req},
+
/* 4.5.1.1 MM Connection (EST) */
{SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_first},
@@ -3677,6 +4295,12 @@ static struct downstate {
{SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_first},
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_first},
+
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_first},
+
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_more},
@@ -3695,6 +4319,7 @@ static struct downstate {
{SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES,
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_wait},
+ /* Reject call in other states. */
{ALL_STATES, ALL_STATES,
GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_reject},
@@ -3704,6 +4329,18 @@ static struct downstate {
{ALL_STATES, ALL_STATES,
GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_reject},
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_reject},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_reject},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_reject},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_reject},
+
/* 4.5.2.1 MM Connection (DATA) */
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
@@ -3717,6 +4354,14 @@ static struct downstate {
SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
GSM48_MMSMS_DATA_REQ, gsm48_mm_data},
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMGCC_DATA_REQ, gsm48_mm_data},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMBCC_DATA_REQ, gsm48_mm_data},
+
/* 4.5.2.1 MM Connection (REL) */
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
GSM48_MMCC_REL_REQ, gsm48_mm_release_active},
@@ -3727,6 +4372,12 @@ static struct downstate {
{SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
GSM48_MMSMS_REL_REQ, gsm48_mm_release_active},
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_release_active},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMBCC_REL_REQ, gsm48_mm_release_active},
+
{SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_add},
@@ -3736,6 +4387,12 @@ static struct downstate {
{SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_add},
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_release_wait_add},
+
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMBCC_REL_REQ, gsm48_mm_release_wait_add},
+
{SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES,
GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_active},
@@ -3753,6 +4410,35 @@ static struct downstate {
{SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_rr},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_release_wait_rr},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
+ GSM48_MMBCC_REL_REQ, gsm48_mm_release_wait_rr},
+
+ /* Group transmit mode */
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES,
+ GSM48_MMGCC_UPLINK_REL_REQ, gsm48_mm_uplink_rel_req},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES,
+ GSM48_MMBCC_UPLINK_REL_REQ, gsm48_mm_uplink_rel_req},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES,
+ GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES,
+ GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES,
+ GSM48_MMGCC_DATA_REQ, gsm48_mm_data},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES,
+ GSM48_MMBCC_DATA_REQ, gsm48_mm_data},
};
#define DOWNSLLEN \
@@ -3767,7 +4453,7 @@ int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg)
int i, rc;
/* keep up to date with the transaction ID */
- conn = mm_conn_by_ref(mm, mmh->ref);
+ conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK));
if (conn)
conn->transaction_id = mmh->transaction_id;
@@ -3880,6 +4566,23 @@ static struct rrdatastate {
SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* not supported */
GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con},
+ /* Group call */
+ {ALL_STATES,
+ GSM48_RR_GROUP_CNF, gsm48_mm_group_cnf},
+
+ {ALL_STATES,
+ GSM48_RR_UPLINK_CNF, gsm48_mm_uplink_cnf},
+
+ {ALL_STATES,
+ GSM48_RR_GROUP_REL_IND, gsm48_mm_group_rel_ind},
+
+ {ALL_STATES,
+ GSM48_RR_UPLINK_REL_IND, gsm48_mm_uplink_rel_ind},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS),
+ GSM48_RR_REL_IND, gsm48_mm_group_rel_ind},
+
/* other (also wait for network command) */
{ALL_STATES,
GSM48_RR_REL_IND, gsm48_mm_rel_other},
@@ -3965,20 +4668,84 @@ static struct mmdatastate {
#define MMDATASLLEN \
(sizeof(mmdatastatelist) / sizeof(struct mmdatastate))
+static int create_conn_and_push_mm_hdr(struct gsm48_mmlayer *mm, struct msgb *msg, int rr_est, int rr_prim,
+ uint8_t sapi)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+ uint8_t transaction_id;
+ uint32_t callref;
+ struct gsm48_mm_conn *conn;
+ struct gsm48_mmxx_hdr *mmh;
+
+ transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */
+
+ if (mm->vgcs.enabled) {
+ /* Ongoing group call. */
+ callref = mm->vgcs.callref;
+ } else {
+ /* find transaction, if any */
+ conn = mm_conn_by_id(mm, pdisc, transaction_id);
+
+ /* create MM connection instance */
+ if (!conn) {
+ /* if MT calls are not supported with protocol */
+ if (rr_est == -1) {
+ LOGP(DMM, LOGL_ERROR, "No MO connection for pdisc=%d, transaction_id=%d\n",
+ pdisc, transaction_id);
+ return -EINVAL;
+ }
+ conn = mm_conn_new(mm, pdisc, transaction_id, sapi, mm_conn_new_ref++);
+ rr_prim = rr_est;
+ }
+ if (!conn)
+ return -ENOMEM;
+ callref = conn->ref;
+ }
+
+ /* push new header */
+ msgb_push(msg, sizeof(struct gsm48_mmxx_hdr));
+ mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ mmh->msg_type = rr_prim;
+ mmh->ref = callref;
+ mmh->transaction_id = transaction_id;
+ mmh->sapi = sapi;
+
+ /* go MM CONN ACTIVE state */
+ if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD ||
+ mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) {
+ /* stop RR release timer */
+ stop_mm_t3240(mm);
+
+ /* stop "RR connection release not allowed" timer */
+ stop_mm_t3241(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+ }
+
+ return 0;
+}
+
static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
uint8_t sapi = rrh->sapi;
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gh->proto_discr & 0x0f;
- uint8_t msg_type = gh->msg_type & 0xbf;
- struct gsm48_mmxx_hdr *mmh;
+ const struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc, msg_type;
int msg_supported = 0; /* determine, if message is supported at all */
- int rr_prim = -1, rr_est = -1; /* no prim set */
uint8_t skip_ind;
int i, rc;
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOGP(DMM, LOGL_INFO, "%s(): short read of msgb: %s\n",
+ __func__, msgb_hexdump(msg));
+ return -EINVAL;
+ }
+
+ pdisc = gh->proto_discr & 0x0f;
+ msg_type = gh->msg_type & 0xbf;
+
/* 9.2.19 */
if (msg_type == GSM48_MT_MM_NULL) {
msgb_free(msg);
@@ -3994,61 +4761,6 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
/* pull the RR header */
msgb_pull(msg, sizeof(struct gsm48_rr_hdr));
- /* create transaction (if not exists) and push message */
- switch (pdisc) {
- case GSM48_PDISC_CC:
- rr_prim = GSM48_MMCC_DATA_IND;
- rr_est = GSM48_MMCC_EST_IND;
- break;
- case GSM48_PDISC_NC_SS:
- rr_prim = GSM48_MMSS_DATA_IND;
- rr_est = GSM48_MMSS_EST_IND;
- break;
- case GSM48_PDISC_SMS:
- rr_prim = GSM48_MMSMS_DATA_IND;
- rr_est = GSM48_MMSMS_EST_IND;
- break;
- }
- if (rr_prim != -1) {
- uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
- /* flip */
- struct gsm48_mm_conn *conn;
-
- /* find transaction, if any */
- conn = mm_conn_by_id(mm, pdisc, transaction_id);
-
- /* create MM connection instance */
- if (!conn) {
- conn = mm_conn_new(mm, pdisc, transaction_id, sapi,
- mm_conn_new_ref++);
- rr_prim = rr_est;
- }
- if (!conn) {
- msgb_free(msg);
- return -ENOMEM;
- }
-
- /* push new header */
- msgb_push(msg, sizeof(struct gsm48_mmxx_hdr));
- mmh = (struct gsm48_mmxx_hdr *)msg->data;
- mmh->msg_type = rr_prim;
- mmh->ref = conn->ref;
- mmh->transaction_id = conn->transaction_id;
- mmh->sapi = conn->sapi;
-
- /* go MM CONN ACTIVE state */
- if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD
- || mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) {
- /* stop RR release timer */
- stop_mm_t3240(mm);
-
- /* stop "RR connection release not allowed" timer */
- stop_mm_t3241(mm);
-
- new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
- }
- }
-
/* forward message */
switch (pdisc) {
case GSM48_PDISC_MM:
@@ -4062,23 +4774,42 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
break; /* follow the selection procedure below */
case GSM48_PDISC_CC:
- rc = gsm48_rcv_cc(ms, msg);
+ rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMCC_EST_IND, GSM48_MMCC_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm48_rcv_cc(ms, msg);
msgb_free(msg);
return rc;
case GSM48_PDISC_NC_SS:
- rc = gsm480_rcv_ss(ms, msg);
+ rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMSS_EST_IND, GSM48_MMSS_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm480_rcv_ss(ms, msg);
msgb_free(msg);
return rc;
case GSM48_PDISC_SMS:
- rc = gsm411_rcv_sms(ms, msg);
+ rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMSMS_EST_IND, GSM48_MMSMS_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm411_rcv_sms(ms, msg);
+ msgb_free(msg);
+ return rc;
+
+ case GSM48_PDISC_GROUP_CC:
+ rc = create_conn_and_push_mm_hdr(mm, msg, -1, GSM48_MMGCC_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm44068_rcv_gcc_bcc(ms, msg);
+ msgb_free(msg);
+ return rc;
+ case GSM48_PDISC_BCAST_CC:
+ rc = create_conn_and_push_mm_hdr(mm, msg, -1, GSM48_MMBCC_DATA_IND, sapi);
+ if (rc == 0)
+ rc = gsm44068_rcv_gcc_bcc(ms, msg);
msgb_free(msg);
return rc;
default:
LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n",
- pdisc);
+ pdisc);
msgb_free(msg);
return gsm48_mm_tx_mm_status(ms,
GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
@@ -4160,6 +4891,10 @@ static struct eventstate {
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start},
+ /* 4.2.2.7 Receiving Group Call (Normal service) */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL),
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs},
+
/* 4.2.2.2 Attempt to update / Loc. upd. needed */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
@@ -4195,6 +4930,10 @@ static struct eventstate {
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */
+ /* 4.2.2.8 Receiving Group Call (Limited service) */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs},
+
/* 4.2.2.4 No IMSI */
/* 4.2.2.5 PLMN search, normal service */
{SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
@@ -4230,12 +4969,15 @@ static struct eventstate {
{SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, /* silently detach */
GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_end},
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) |
+ SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS), ALL_STATES, /* uplink access */
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs},
+
{SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) |
SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
SBIT(GSM48_MM_ST_PROCESS_CM_SERV_P) |
SBIT(GSM48_MM_ST_WAIT_REEST) |
SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON) |
- SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) |
SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, /* we can release */
GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_release},
@@ -4290,6 +5032,17 @@ static struct eventstate {
{ALL_STATES, ALL_STATES,
GSM48_MM_EVENT_CLASSMARK_CHG, gsm48_mm_classm_chg},
#endif
+
+ /* Group call notification event */
+ {ALL_STATES, ALL_STATES,
+ GSM48_MM_EVENT_NOTIFICATION, gsm48_mm_notification},
+
+ /* Uplink free/busy while in group receive mode */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL) | SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MM_EVENT_UPLINK_BUSY, gsm48_mm_uplink_free},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL) | SBIT(GSM48_MM_SST_RX_VGCS_LIMITED),
+ GSM48_MM_EVENT_UPLINK_FREE, gsm48_mm_uplink_free},
};
#define EVENTSLLEN \
@@ -4318,7 +5071,8 @@ static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg)
&& ((1 << mm->substate) & eventstatelist[i].substates))
break;
if (i == EVENTSLLEN) {
- LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n");
+ LOGP(DMM, LOGL_NOTICE, "Message %s unhandled in state %s.\n",
+ get_mmevent_name(msg_type), gsm48_mm_state_names[mm->state]);
return 0;
}
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c
index e115d159..83287c14 100644
--- a/src/host/layer23/src/mobile/gsm48_rr.c
+++ b/src/host/layer23/src/mobile/gsm48_rr.c
@@ -13,10 +13,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.
- *
*/
/* Very short description of some of the procedures:
@@ -38,6 +34,53 @@
* When the assignment or handover fails, the old channel is activate and the
* link is established again. Also pending messages are sent.
*
+ * Group Channel:
+ *
+ * A group receive/transmit mode is not documented at GSM 04.07. The group
+ * receive mode is similar to the IDLE mode, except that the MS is listening
+ * to a TCH. The group transmit mode is similar to the DEDICATED mode. Special
+ * (undocumented) commands are used to enter group receive and transmit mode.
+ *
+ * There is a substate that indicates group receive/transmit mode. If the
+ * substate is set to group receive, the IDLE mode becomes the group receive
+ * mode. If the substate is set to group transmit, the dedicated mode becomes
+ * the group transmit mode. The substate set to group receive allows to enter
+ * regular dedicated mode and return back to group receive mode afterwards.
+ *
+ * new_rr_state(rr, GSM48_RR_ST_IDLE) is used to return to IDLE mode or to
+ * group receive mode, depending on the substate.
+ *
+ * The Uplink access on group channel:
+ *
+ * If the uplink is busy, wait until it becomes free (Uplink investigation
+ * procedure). Abort, if the procedure times out, if the VGCS UPLINK GRANT
+ * message is recived for a different talker.
+ *
+ * Request uplink using access bursts on TCH until an VGCS UPLINK GRANT is
+ * received. Abort, if it the procedure times out, if the uplink becomes busy,
+ * if the VGCS UPLINK GRANT message references a different frame numer.
+ *
+ * Establish layer 2 with TALKER INDICATION. Abort, if content resolution
+ * mismatches (RR_REL_IND), if link fails (MDL_ERROR), if uplink becomes free.
+ *
+ * Release uplink and wait until uplink becomes free. Abort, if UPLINK RELEASE
+ * is received or if uplink fails.
+ *
+ * New primitives are invented for group/broadcast calls. They are not
+ * specified in any recommendation. They are:
+ *
+ * GSM48_MM_EVENT_NOTIFICATION: Notify MM layer about received/ceased call.
+ * GSM48_MM_EVENT_UPLINK_BUSY: Notify MM layer about uplink becoming busy.
+ * GSM48_MM_EVENT_UPLINK_FREE: Notify MM layer about uplink becoming free.
+ *
+ * RR_GROUP_REQ: The MM layer requests group channel in receive mode.
+ * RR_GROUP_CNF: The RR confirms group channel.
+ * RR_GROUP_REL_REQ: The MM layer releases group channel.
+ * RR_GROUP_REL_IND: The RR indicates/confirms release of group channel.
+ * RR_UPLINK_REQ: The MM layer requests uplink (group transmit mode).
+ * RR_UPLINK_CNF: The RR layer confirms uplink. (Uplink was granted.)
+ * RR_UPLINK_REL_REQ: The MM layer requests release of uplink.
+ * RR_UPLINK_REL_IND: The RR layer indicates/confirms release of uplink
*/
/* Testing delayed (immediate) assignment / handover
@@ -71,36 +114,51 @@
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/bitvec.h>
-#include <osmocom/codec/codec.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/l1ctl.h>
-#include <osmocom/bb/mobile/vty.h>
#include <osmocom/bb/common/utils.h>
+#include <osmocom/bb/common/settings.h>
+
+#include <osmocom/bb/mobile/vty.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
#include <l1ctl_proto.h>
+/* Check response for the last 3 channel requests only. See TS 44.018 §3.3.1.1.3.1 and §3.3.1.1.3.2. */
+#define IMM_ASS_HISTORY 3
+/* Check response for up to 5 uplink requests. See TS 44.018 §3.3.1.2.1.2. */
+#define UL_GRANT_HISTROY 5
+
+static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t *ma_len);
+static int gsm48_rr_activate_channel(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len);
static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro);
static void stop_rr_t_starting(struct gsm48_rrlayer *rr);
static void stop_rr_t3124(struct gsm48_rrlayer *rr);
static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_rr_dl_est(struct osmocom_ms *ms);
static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms);
-static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
- uint8_t mode);
+static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags);
static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg);
int gsm414_rcv_test(struct osmocom_ms *ms, const struct msgb *msg);
+static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause);
+static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause);
+static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event);
+static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num);
+static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms);
+static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic);
+static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg);
/*
* support
*/
-#define MIN(a, b) ((a < b) ? a : b)
-
/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */
static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc,
uint8_t *power_level, uint8_t *atc)
@@ -251,8 +309,14 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
struct gsm_settings *set = &ms->settings;
uint8_t ch_type, ch_subch, ch_ts;
+ if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, chan_nr);
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+
/* only complain if we use TCH/F or TCH/H */
- rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts);
if (ch_type != RSL_CHAN_Bm_ACCHs
&& ch_type != RSL_CHAN_Lm_ACCHs)
return 0;
@@ -309,6 +373,56 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n");
}
break;
+ case GSM48_CMODE_DATA_14k5:
+ if (ch_type != RSL_CHAN_Bm_ACCHs) {
+ LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ } else if (!set->csd_tch_f144) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F14.4 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/F14.4 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ break;
+ case GSM48_CMODE_DATA_12k0:
+ if (ch_type != RSL_CHAN_Bm_ACCHs) {
+ LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ } else if (!set->csd_tch_f96) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F9.6 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/F9.6 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f48)
+ || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h48)) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c4.8 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/%c4.8 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ break;
+ case GSM48_CMODE_DATA_3k6:
+ if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f24)
+ || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h24)) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c2.4 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/%c2.4 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ break;
default:
LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode);
return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
@@ -345,19 +459,21 @@ const char *gsm48_rr_state_names[] = {
static void new_rr_state(struct gsm48_rrlayer *rr, int state)
{
+ struct osmocom_ms *ms = rr->ms;
+
if (state < 0 || state >=
(sizeof(gsm48_rr_state_names) / sizeof(char *)))
return;
- /* must check against equal state */
- if (rr->state == state) {
+ /* Check against equal state or IDLE state. */
+ if (rr->state == state && state != GSM48_RR_ST_IDLE) {
LOGP(DRR, LOGL_INFO, "equal state ? %s\n",
gsm48_rr_state_names[rr->state]);
return;
}
LOGP(DRR, LOGL_INFO, "new state %s -> %s\n",
- gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
+ gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
/* abort handover, in case of release of dedicated mode */
if (rr->state == GSM48_RR_ST_DEDICATED) {
@@ -371,10 +487,15 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
rr->state = state;
- if (state == GSM48_RR_ST_IDLE) {
+ if (state != GSM48_RR_ST_IDLE)
+ return;
+
+ /* Return from dedicated/group receive/transmit mode to idle mode. (Trigger cell reselection.) */
+ if (rr->vgcs.group_state == GSM48_RR_GST_OFF) {
struct msgb *msg, *nmsg;
struct gsm322_msg *em;
+ LOGP(DRR, LOGL_INFO, "Returning to IDLE mode.\n");
/* release dedicated mode, if any */
l1ctl_tx_dm_rel_req(rr->ms);
rr->ms->meas.rl_fail = 0;
@@ -413,6 +534,41 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state)
msgb_free(nmsg);
/* reset any BA range */
rr->ba_ranges = 0;
+ return;
+ }
+
+ /* Return from dedicated mode to group receive mode.
+ * This is not used, because we never enter dedicated mode during group receive/transmit mode.
+ * It may be used later, if we support it in MM layer. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
+ uint16_t ma[64];
+ uint8_t ma_len;
+ struct msgb *msg;
+
+ LOGP(DRR, LOGL_INFO, "Returning to GROUP RECEIVE mode.\n");
+ /* release dedicated mode, if any */
+ l1ctl_tx_dm_rel_req(rr->ms);
+ rr->ms->meas.rl_fail = 0;
+ rr->dm_est = 0;
+ l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED);
+ /* free establish message, if any */
+ rr->rr_est_req = 0;
+ if (rr->rr_est_msg) {
+ msgb_free(rr->rr_est_msg);
+ rr->rr_est_msg = NULL;
+ }
+ /* free all pending messages */
+ while ((msg = msgb_dequeue(&rr->downqueue)))
+ msgb_free(msg);
+ /* reset ciphering */
+ rr->cipher_on = 0;
+ /* copy channel description "group mode" */
+ memcpy(&rr->cd_now, &rr->vgcs.cd_group, sizeof(rr->cd_now));
+ /* render channel "group mode" */
+ gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len);
+ /* activate channel */
+ gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len);
+ return;
}
}
@@ -453,6 +609,14 @@ static const struct value_string gsm48_rr_msg_names[] = {
{ GSM48_RR_ABORT_REQ, "RR_ABORT_REQ" },
{ GSM48_RR_ABORT_IND, "RR_ABORT_IND" },
{ GSM48_RR_ACT_REQ, "RR_ACT_REQ" },
+ { GSM48_RR_GROUP_REQ, "RR_GROUP_REQ" },
+ { GSM48_RR_GROUP_CNF, "RR_GROUP_CNF" },
+ { GSM48_RR_GROUP_REL_REQ, "RR_GROUP_REL_REQ" },
+ { GSM48_RR_GROUP_REL_IND, "RR_GROUP_REL_IND" },
+ { GSM48_RR_UPLINK_REQ, "RR_UPLINK_REQ" },
+ { GSM48_RR_UPLINK_CNF, "RR_UPLINK_CNF" },
+ { GSM48_RR_UPLINK_REL_REQ, "RR_UPLINK_REL_REQ" },
+ { GSM48_RR_UPLINK_REL_IND, "RR_UPLINK_REL_IND" },
{ 0, NULL }
};
@@ -634,39 +798,43 @@ static void timeout_rr_meas(void *arg)
struct gsm_settings *set = &rr->ms->settings;
int rxlev, berr, snr;
uint8_t ch_type, ch_subch, ch_ts;
+ struct osmo_strbuf sb;
char text[256];
+ sb = (struct osmo_strbuf) { .buf = text, .len = sizeof(text) };
+
/* don't monitor if no cell is selected or if we scan neighbour cells */
if (!cs->selected || cs->neighbour) {
- sprintf(text, "MON: not camping on serving cell");
+ OSMO_STRBUF_PRINTF(sb, "MON: not camping on serving cell");
goto restart;
} else if (!meas->frames) {
- sprintf(text, "MON: no cell info");
+ OSMO_STRBUF_PRINTF(sb, "MON: no cell info");
} else {
rxlev = (meas->rxlev + meas->frames / 2) / meas->frames;
berr = (meas->berr + meas->frames / 2) / meas->frames;
snr = (meas->snr + meas->frames / 2) / meas->frames;
- sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d "
- "LAI=%s %s %04x ID=%04x", cs->sel_arfcn,
- gsm_print_rxlev(rxlev), snr, berr,
- gsm_print_mcc(cs->sel_mcc),
- gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id);
+ OSMO_STRBUF_PRINTF(sb, "MON: f=%d lev=%s snr=%2d ber=%3d "
+ "CGI=%s", cs->sel_arfcn,
+ gsm_print_rxlev(rxlev), snr, berr,
+ osmo_cgi_name(&cs->sel_cgi));
if (rr->state == GSM48_RR_ST_DEDICATED) {
- rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type,
- &ch_subch, &ch_ts);
- sprintf(text + strlen(text), " TA=%d pwr=%d TS=%d",
- rr->cd_now.ind_ta - set->alter_delay,
- (set->alter_tx_power) ? set->alter_tx_power_value
- : rr->cd_now.ind_tx_power, ch_ts);
- if (ch_type == RSL_CHAN_SDCCH8_ACCH
- || ch_type == RSL_CHAN_SDCCH4_ACCH)
- sprintf(text + strlen(text), "/%d", ch_subch);
+ OSMO_STRBUF_PRINTF(sb, " TA=%d pwr=%d",
+ rr->cd_now.ind_ta - set->alter_delay,
+ (set->alter_tx_power) ? set->alter_tx_power_value
+ : rr->cd_now.ind_tx_power);
+ if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) == 0) {
+ OSMO_STRBUF_PRINTF(sb, " TS=%d", ch_ts);
+ if (ch_type == RSL_CHAN_SDCCH8_ACCH
+ || ch_type == RSL_CHAN_SDCCH4_ACCH
+ || ch_type == RSL_CHAN_Lm_ACCHs)
+ OSMO_STRBUF_PRINTF(sb, "/%d", ch_subch);
+ }
} else
gsm322_meas(rr->ms, rxlev);
}
LOGP(DRR, LOGL_INFO, "%s\n", text);
if (rr->monitor)
- vty_notify(rr->ms, "%s\n", text);
+ l23_vty_ms_notify(rr->ms, "%s\n", text);
if (rr->dm_est)
gsm48_rr_tx_meas_rep(rr->ms);
@@ -767,6 +935,36 @@ static void timeout_rr_t3126(void *arg)
new_rr_state(rr, GSM48_RR_ST_IDLE);
}
+static void timeout_rr_t3128(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+ struct osmocom_ms *ms = rr->ms;
+
+ LOGP(DRR, LOGL_INFO, "timer T3128 has fired\n");
+
+ LOGP(DRR, LOGL_NOTICE, "Failed to access uplink, uplink did not become free.\n");
+ gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
+}
+
+static void timeout_rr_t3130(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+ struct osmocom_ms *ms = rr->ms;
+
+ LOGP(DRR, LOGL_INFO, "timer T3130 has fired\n");
+
+ gsm48_rr_uplink_access(ms, NULL);
+}
+
+static void timeout_rr_t_ul_free(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+
+ LOGP(DRR, LOGL_INFO, "uplink free timer has fired\n");
+ rr->vgcs.uplink_free = false;
+ gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY);
+}
+
static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro)
{
rr->t_meas.cb = timeout_rr_meas;
@@ -819,6 +1017,33 @@ static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
osmo_timer_schedule(&rr->t3126, sec, micro);
}
+static void start_rr_t3128(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T3128 with %d.%03d seconds\n", sec,
+ micro / 1000);
+ rr->vgcs.t3128.cb = timeout_rr_t3128;
+ rr->vgcs.t3128.data = rr;
+ osmo_timer_schedule(&rr->vgcs.t3128, sec, micro);
+}
+
+static void start_rr_t3130(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T3130 with %d.%03d seconds\n", sec,
+ micro / 1000);
+ rr->vgcs.t3130.cb = timeout_rr_t3130;
+ rr->vgcs.t3130.data = rr;
+ osmo_timer_schedule(&rr->vgcs.t3130, sec, micro);
+}
+
+static void start_rr_t_ul_free(struct gsm48_rrlayer *rr)
+{
+ if (!osmo_timer_pending(&rr->vgcs.t_ul_free))
+ LOGP(DRR, LOGL_INFO, "starting uplink free timer\n");
+ rr->vgcs.t_ul_free.cb = timeout_rr_t_ul_free;
+ rr->vgcs.t_ul_free.data = rr;
+ osmo_timer_schedule(&rr->vgcs.t_ul_free, 0, 480000);
+}
+
static void stop_rr_t_meas(struct gsm48_rrlayer *rr)
{
if (osmo_timer_pending(&rr->t_meas)) {
@@ -875,6 +1100,30 @@ static void stop_rr_t3126(struct gsm48_rrlayer *rr)
}
}
+static void stop_rr_t3128(struct gsm48_rrlayer *rr)
+{
+ if (osmo_timer_pending(&rr->vgcs.t3128)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T3128\n");
+ osmo_timer_del(&rr->vgcs.t3128);
+ }
+}
+
+static void stop_rr_t3130(struct gsm48_rrlayer *rr)
+{
+ if (osmo_timer_pending(&rr->vgcs.t3130)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T3130\n");
+ osmo_timer_del(&rr->vgcs.t3130);
+ }
+}
+
+static void stop_rr_t_ul_free(struct gsm48_rrlayer *rr)
+{
+ if (osmo_timer_pending(&rr->vgcs.t_ul_free)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending uplink free timer\n");
+ osmo_timer_del(&rr->vgcs.t_ul_free);
+ }
+}
+
/*
* status
*/
@@ -910,11 +1159,9 @@ int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
/* send chiperhing mode complete */
static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
{
- struct gsm_settings *set = &ms->settings;
struct msgb *nmsg;
struct gsm48_hdr *gh;
struct gsm48_rr_hdr *nrrh;
- uint8_t buf[11], *tlv;
LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr);
@@ -927,13 +1174,8 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL;
/* MI */
- if (cr) {
- gsm48_generate_mid_from_imsi(buf, set->imeisv);
- /* alter MI type */
- buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | GSM_MI_TYPE_IMEISV;
- tlv = msgb_put(nmsg, 2 + buf[1]);
- memcpy(tlv, buf, 2 + buf[1]);
- }
+ if (cr)
+ gsm48_encode_mi_tlv(ms, nmsg, GSM_MI_TYPE_IMEISV, false);
gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
@@ -1280,6 +1522,648 @@ static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg)
}
/*
+ * ASCI notification
+ */
+
+struct asci_notif {
+ struct llist_head entry;
+ struct gsm48_rrlayer *rr;
+ uint8_t gcr[5];
+ bool ch_desc_present;
+ struct gsm48_chan_desc ch_desc;
+ struct osmo_timer_list timer;
+};
+
+/* When does a notification received from NCH expires. */
+#define NOTIFICATION_TIMEOUT 5
+
+static void asci_notif_timeout(void *arg);
+
+/* Add new notification to list. */
+static struct asci_notif *asci_notif_alloc(struct gsm48_rrlayer *rr, const uint8_t *gcr)
+{
+ struct asci_notif *notif;
+
+ notif = talloc_zero(rr->ms, struct asci_notif);
+ if (!notif)
+ return NULL;
+ notif->rr = rr;
+ memcpy(notif->gcr, gcr, sizeof(notif->gcr));
+ llist_add_tail(&notif->entry, &rr->vgcs.notif_list);
+
+ notif->timer.cb = asci_notif_timeout;
+ notif->timer.data = notif;
+
+ return notif;
+}
+
+/* Remove notification from list. */
+static void asci_notif_free(struct asci_notif *notif)
+{
+ osmo_timer_del(&notif->timer);
+ llist_del(&notif->entry);
+ talloc_free(notif);
+}
+
+/* Remove all ASCI notifications from list. */
+static void asci_notif_list_free(struct gsm48_rrlayer *rr)
+{
+ struct asci_notif *notif, *notif2;
+
+ llist_for_each_entry_safe(notif, notif2, &rr->vgcs.notif_list, entry)
+ asci_notif_free(notif);
+}
+
+/* Notification timed out. */
+static void asci_notif_timeout(void *arg)
+{
+ struct asci_notif *notif = arg;
+ struct gsm48_rrlayer *rr = notif->rr;
+ struct msgb *nmsg;
+ struct gsm48_mm_event *mme;
+
+ /* Send notification of ceased call to MM layer. */
+ LOGP(DRR, LOGL_INFO, "Notify MM layer about ceased group call.\n");
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION);
+ if (!nmsg)
+ return;
+ mme = (struct gsm48_mm_event *) nmsg->data;
+ memcpy(mme->notification.gcr, notif->gcr, sizeof(mme->notification.gcr));
+ mme->notification.gone = true;
+ gsm48_mmevent_msg(rr->ms, nmsg);
+
+ asci_notif_free(notif);
+}
+
+/* Find notification in list. */
+static struct asci_notif *asci_notif_find(struct gsm48_rrlayer *rr, const uint8_t *gcr)
+{
+ struct asci_notif *notif;
+
+ llist_for_each_entry(notif, &rr->vgcs.notif_list, entry) {
+ if (!memcmp(&notif->gcr, gcr, sizeof(notif->gcr)))
+ return notif;
+ }
+
+ return NULL;
+}
+
+static int gsm48_rr_rx_group_call(struct osmocom_ms *ms, const uint8_t *gcr, const uint8_t *ch_desc, bool nch)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct asci_notif *notif;
+ bool update_call = false;
+ struct msgb *nmsg;
+ struct gsm48_mm_event *mme;
+
+ /* Find or create notification entry. Only create entries for notifications on NCH */
+ notif = asci_notif_find(rr, gcr);
+ if (!notif) {
+ update_call = true;
+ if (nch) {
+ notif = asci_notif_alloc(rr, gcr);
+ if (!notif)
+ return -ENOMEM;
+ }
+ }
+
+ /* Update channel description. */
+ if (notif) {
+ if (ch_desc) {
+ if (!notif->ch_desc_present)
+ update_call = true;
+ notif->ch_desc_present = true;
+ memcpy(&notif->ch_desc, ch_desc, sizeof(notif->ch_desc));
+ } else {
+ notif->ch_desc_present = false;
+ if (!notif->ch_desc_present)
+ update_call = true;
+ }
+ /* (Re-)Start timer. */
+ osmo_timer_schedule(&notif->timer, NOTIFICATION_TIMEOUT, 0);
+ } else
+ update_call = true;
+
+ /* Send notification of new or updated call to MM layer. */
+ if (update_call) {
+ LOGP(DRR, LOGL_INFO, "Notify MM layer about new/updated group call.\n");
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION);
+ if (!nmsg)
+ return -ENOMEM;
+ mme = (struct gsm48_mm_event *) nmsg->data;
+ memcpy(mme->notification.gcr, gcr, sizeof(mme->notification.gcr));
+ if (ch_desc) {
+ mme->notification.ch_desc_present = true;
+ memcpy(&mme->notification.ch_desc, ch_desc, sizeof(mme->notification.ch_desc));
+ }
+ gsm48_mmevent_msg(ms, nmsg);
+ }
+
+ return 0;
+}
+
+/* Common function to decode Group Call Information */
+static int gsm48_rr_decode_group_call_info(struct osmocom_ms *ms, struct bitvec *bv, bool nch)
+{
+ struct bitvec *gcr_bv = NULL, *chd_bv = NULL;
+ int rc = 0;
+ int i;
+
+ /* <Group Call Reference : bit(36)> */
+ gcr_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(36), NULL);
+ OSMO_ASSERT(gcr_bv);
+ for (i = 0; i < 36; i++)
+ bitvec_set_bit(gcr_bv, bitvec_get_bit_pos(bv, bv->cur_bit++));
+ /* Group Channel Description */
+ if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) {
+ chd_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(24), NULL);
+ OSMO_ASSERT(chd_bv);
+ for (i = 0; i < 24; i++)
+ bitvec_set_bit(chd_bv, bitvec_get_bit_pos(bv, bv->cur_bit++));
+ /* FIXME: hopping */
+ if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) {
+ LOGP(DRR, LOGL_ERROR, "Hopping not supported on VGCS/VBS channel, please fix!\n");
+ rc = -ENOTSUP;
+ goto out;
+ }
+ }
+
+ rc = gsm48_rr_rx_group_call(ms, gcr_bv->data, (chd_bv) ? chd_bv->data : NULL, nch);
+
+out:
+ bitvec_free(chd_bv);
+ bitvec_free(gcr_bv);
+
+ return rc;
+}
+
+/* Notification/FACCH (9.1.21a) */
+static int gsm48_rr_rx_notif_facch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr_sh *sgh = msgb_l3(msg);
+ int payload_len = msgb_l3len(msg) - sizeof(*sgh);
+ struct bitvec bv;
+ int rc;
+
+ LOGP(DRR, LOGL_INFO, "NOTIFICATION/FACCH\n");
+
+ bv = (struct bitvec) {
+ .data = sgh->data,
+ .data_len = payload_len,
+ };
+
+ /* Group Call Information */
+ if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 0) {
+ rc = gsm48_rr_decode_group_call_info(ms, &bv, false);
+ return rc;
+ }
+
+ /* Note: Other information are not used. */
+ return -ENOTSUP;
+}
+
+/* Notification/NCH (9.1.21b) */
+static int gsm48_rr_rx_notif_nch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_notification_nch *nn = msgb_l3(msg);
+ int payload_len = msgb_l3len(msg) - sizeof(*nn);
+ struct bitvec bv;
+ int rc;
+
+ LOGP(DRR, LOGL_INFO, "NOTIFICATION/NCH\n");
+
+ bv = (struct bitvec) {
+ .data = nn->data,
+ .data_len = payload_len,
+ };
+
+ /* 0 | 1 <NLN(NCH) : bit (2) > */
+ if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) {
+ /* NLN not used
+ nln = bitvec_get_uint(&bv, 2);
+ nln_present = true;
+ */
+ bv.cur_bit += 2;
+ }
+
+ /* < list of Group Call NCH information > */
+ while (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) {
+ rc = gsm48_rr_decode_group_call_info(ms, &bv, true);
+ if (rc < 0)
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * VGCS uplink control
+ */
+
+/* Send uplink status to upper layer. */
+static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+
+ if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE)
+ return -EINVAL;
+
+ /* Send notification of uplink state to MM layer. */
+ LOGP(DRR, LOGL_INFO, "Notify MM layer about uplink state.\n");
+ nmsg = gsm48_mmevent_msgb_alloc(event);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(rr->ms, nmsg);
+
+ return 0;
+}
+
+/* UPLINK BUSY (9.1.46) */
+static int gsm48_rr_rx_uplink_busy(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ LOGP(DRR, LOGL_INFO, "UPLINK BUSY\n");
+
+ /* Only allow when in some group mode. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
+ return 0;
+
+ /* Uplink is busy now. */
+ if (rr->vgcs.uplink_free) {
+ rr->vgcs.uplink_free = false;
+ gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_BUSY);
+ }
+ stop_rr_t_ul_free(rr);
+
+ /* Abort during uplink investigation or access procedure. */
+ if (osmo_timer_pending(&rr->vgcs.t3128) || osmo_timer_pending(&rr->vgcs.t3130)) {
+ LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due to busy uplink.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
+ }
+
+ return 0;
+}
+
+/* UPLINK FREE (9.1.47) */
+static int gsm48_rr_rx_uplink_free(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct bitvec bv;
+ bool uplink_access = false;
+ uint8_t uic = 0xff;
+ uint8_t *mode;
+
+ bv = (struct bitvec) {
+ .data_len = msgb_l3len(msg),
+ .data = msgb_l3(msg),
+ .cur_bit = 8,
+ };
+
+ /* Uplink Access */
+ if (bitvec_get_bit_high(&bv) == H)
+ uplink_access = true;
+
+ /* UIC */
+ if (bitvec_get_bit_high(&bv) == H)
+ uic = bitvec_get_uint(&bv, 6);
+
+ /* Note: Emergency Indicator not used. */
+
+ /* Do not flood the logging with UPLINK FREE messages. Log only on the fist received message. */
+ if (!osmo_timer_pending(&rr->vgcs.t_ul_free))
+ LOGP(DRR, LOGL_INFO, "UPLINK FREE (uplink access=%s, uic=0x%02x)\n", (uplink_access) ? "true" : "false",
+ uic);
+
+ /* Uplink is free now. */
+ if (!rr->vgcs.uplink_free) {
+ rr->vgcs.uplink_free = true;
+ gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_FREE);
+ }
+ rr->vgcs.uic = uic;
+ rr->vgcs.uplink_access = uplink_access;
+ start_rr_t_ul_free(rr);
+
+ /* We can be in group mode or in dedicated mode. When we are in dedicated mode and we receive UPLINK FREE,
+ * we know that we are actually on a group channel. This is the actual confirm to the UPLINK RELEASE message
+ * on a group channel. We must then release layer 2 locally and indicate channel release toward upper layer.
+ *
+ * When we are in group transmit mode, we return to group receive mode and also release layer 2 locally and
+ * indicate uplink release towards upper layer.
+ *
+ * When we are waiting for a free uplink (T3128 is running), we start uplink access. While accessing the
+ * uplink, we ignore further UPLINK FREE messages.
+ */
+ if (rr->vgcs.group_state != GSM48_RR_GST_OFF) {
+ /* Start uplink access. */
+ if (osmo_timer_pending(&rr->vgcs.t3128)) {
+ /* Stop timer, because uplink is now free. */
+ stop_rr_t3128(rr);
+ rr->vgcs.uplink_tries = 3;
+ return gsm48_rr_uplink_access(ms, NULL);
+ }
+
+ /* Ignore uplink free messages while accessing uplink. */
+ if (osmo_timer_pending(&rr->vgcs.t3130))
+ return 0;
+ }
+
+ /* Any time in group transmit mode or dedicated mode, release on uplink free. */
+ if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) {
+ struct msgb *nmsg;
+
+ LOGP(DRR, LOGL_INFO, "Uplink becomes free, send (local) release to layer 2.\n");
+
+ /* Go into pending release state if not already.
+ * We are already in pending relese state when we release uplink normally.
+ * If we receive UPLINK FREE while we release normally, we abort layer 2. */
+ if (rr->state != GSM48_RR_ST_REL_PEND)
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* release message */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = RSL_REL_LOCAL_END;
+ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+/* UPLINK RELEASE (9.1.48) */
+static int gsm48_rr_rx_uplink_release(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ur);
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short UPLINK RELEASE message.\n");
+ return -EINVAL;
+ }
+
+ LOGP(DRR, LOGL_INFO, "UPLINK RELEASE with cause 0x%02x\n", ur->rr_cause);
+
+ /* Only allow when in some group mode. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
+ return 0;
+
+ if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT)
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL);
+
+ return 0;
+}
+
+/* VGCS UPLINK GRANT (9.1.49) */
+static int gsm48_rr_rx_vgcs_uplink_grant(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
+ struct gsm_settings *set = &ms->settings;
+ struct gsm0408_vgcs_ul_grant *ug = msgb_l3(msg);
+ int ug_len = msgb_l3len(msg) - sizeof(*ug);
+
+ LOGP(DRR, LOGL_INFO, "VGCS UPLINK GRANT\n");
+
+ if (ug_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of VGCS UPLINK GRANT message.\n");
+ return -EINVAL;
+ }
+
+ /* Only allow when in some group mode. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
+ return 0;
+
+ /* Uplink is busy now. */
+ if (rr->vgcs.uplink_free) {
+ rr->vgcs.uplink_free = false;
+ gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY);
+ }
+ stop_rr_t_ul_free(rr);
+
+ /* Abort during uplink investigation or access procedure. */
+ if (osmo_timer_pending(&rr->vgcs.t3128)) {
+ LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone accessing the uplink.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
+ }
+
+ /* We are not waiting for the uplink to be granted. */
+ if (!osmo_timer_pending(&rr->vgcs.t3130))
+ return 0;
+
+ /* Stop timer. */
+ stop_rr_t3130(rr);
+
+ /* Check if message is for our request. */
+ if (!gsm48_match_ra(ms, &ug->req_ref, 5)) {
+ LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone gets uplink access granted.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY);
+ }
+
+ LOGP(DRR, LOGL_INFO, "Access to uplink has been granted.\n");
+
+ /* Set initial power and timing advance. */
+ rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch;
+ rr->cd_now.ind_ta = ug->ta;
+ LOGP(DRR, LOGL_INFO, "Applying initial ta and tx_power\n");
+ l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay,
+ (set->alter_tx_power) ? set->alter_tx_power_value : s->ms_txpwr_max_cch);
+
+ /* Turn on transmitter. */
+ rr->cd_now.tch_flags &= ~(L1CTL_TCH_FLAG_RXONLY);
+ gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags);
+
+ /* Complete group transmit mode. */
+ new_rr_state(rr, GSM48_RR_ST_DEDICATED);
+
+ /* Establish layer 2 connection. */
+ return gsm48_rr_tx_talker_indication(ms);
+}
+
+/* send rr uplink release */
+static int gsm48_rr_tx_uplink_release(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_uplink_release *ur;
+
+ LOGP(DRR, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ ur = (struct gsm48_uplink_release *) msgb_put(nmsg, sizeof(*ur));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE;
+ ur->rr_cause = cause;
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
+}
+
+/* Start uplink access procedure. (3.3.1.2.1.2) */
+static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ /* store frame number */
+ if (msg) {
+ struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
+ struct gsm48_req_ref *ref =
+ (struct gsm48_req_ref *) (ch->data + 1);
+
+ if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) {
+ LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n");
+ return -EINVAL;
+ }
+
+ /* Store to history buffer. */
+ /* shift history and store */
+ memcpy(&(rr->cr_hist[4]), &(rr->cr_hist[3]),
+ sizeof(struct gsm48_cr_hist));
+ memcpy(&(rr->cr_hist[3]), &(rr->cr_hist[2]),
+ sizeof(struct gsm48_cr_hist));
+ memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]),
+ sizeof(struct gsm48_cr_hist));
+ memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]),
+ sizeof(struct gsm48_cr_hist));
+ rr->cr_hist[0].valid = 1;
+ rr->cr_hist[0].ref.ra = rr->cr_ra;
+ rr->cr_hist[0].ref.t1 = ref->t1;
+ rr->cr_hist[0].ref.t2 = ref->t2;
+ rr->cr_hist[0].ref.t3_low = ref->t3_low;
+ rr->cr_hist[0].ref.t3_high = ref->t3_high;
+ }
+
+ if (!osmo_timer_pending(&rr->vgcs.t3130)) {
+ uint8_t uplink_ref;
+
+ /* Only try up to 3 times. */
+ if (!rr->vgcs.uplink_tries) {
+ LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due uplink access timeout.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE);
+ }
+
+ LOGP(DRR, LOGL_INFO, "Trying to access uplink.\n");
+
+ rr->vgcs.uplink_tries--;
+
+ /* See Table 9.1.45.1 */
+ uplink_ref = layer23_random();
+ uplink_ref &= 0x1f;
+ uplink_ref |= 0xc0;
+
+ /* store value, mask and history */
+ rr->cr_ra = uplink_ref;
+ rr->cr_hist[4].valid = 0;
+ rr->cr_hist[3].valid = 0;
+ rr->cr_hist[2].valid = 0;
+ rr->cr_hist[1].valid = 0;
+ rr->cr_hist[0].valid = 0;
+
+ /* Reset counter. */
+ rr->vgcs.uplink_counter = 0;
+
+ /* Start T3130. */
+ start_rr_t3130(rr, GSM_T3130_MS);
+ }
+
+ /* Send random access bursts up to 5 times. */
+ if (rr->vgcs.uplink_counter < 5) {
+ int delay_ms;
+
+ /* The first UPLINK ACCESS message shall be delayed between 0..20ms.
+ * Subsequent UPLINK ACCESS messages shall be delayed 100ms + 0..20ms. */
+ delay_ms = (layer23_random() & 0xffff) / 3277;
+ if (rr->vgcs.uplink_counter)
+ delay_ms += 100;
+
+ gsm48_rr_tx_rand_acc_dedicated(ms, rr->cr_ra, delay_ms * 26 / 120, rr->vgcs.uic);
+ rr->vgcs.uplink_counter++;
+ }
+
+ return 0;
+}
+
+/* Whenever uplink access is released or failed for some reason. */
+static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ /* Stop group transmit mode timers. */
+ stop_rr_t3128(rr);
+ stop_rr_t3130(rr);
+
+ /* Turn off transmitter. */
+ rr->cd_now.tch_flags |= L1CTL_TCH_FLAG_RXONLY;
+ gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags);
+
+ /* Only return IDLE without changing channel, because we are still in group transmit mode. */
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+
+ /* Set group state to receive mode. */
+ rr->vgcs.group_state = GSM48_RR_GST_RECEIVE;
+
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = cause;
+ return gsm48_rr_upmsg(ms, nmsg);
+}
+
+/* send talker indication */
+static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_talker_indication *ti;
+
+ LOGP(DRR, LOGL_INFO, "TALKER INDICATION\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ ti = (struct gsm48_talker_indication *) msgb_put(nmsg, sizeof(*ti));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_TALKER_IND;
+
+ /* classmark 2 */
+ ti->cm2_len = sizeof(ti->cm2);
+ gsm48_rr_enc_cm2(ms, &ti->cm2, rr->cd_now.arfcn);
+
+ /* mobile identity */
+ if (ms->subscr.tmsi != GSM_RESERVED_TMSI && (osmo_lai_cmp(&subscr->lai, &cs->sel_cgi.lai) == 0)) {
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
+ LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with TMSI.\n");
+ } else if (subscr->imsi[0]) {
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
+ LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with IMSI.\n");
+ } else {
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false);
+ LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION without TMSI/IMSI.\n");
+ }
+
+ /* start establishmnet */
+ return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0);
+}
+
+/*
* random access
*/
@@ -1304,7 +2188,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging,
&& (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY
&& cs->state != GSM322_C7_CAMPED_ANY_CELL))) {
LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n");
- return -EINVAL;
+ return -EINVAL;
}
/* ignore channel request while not camping on a cell */
@@ -1485,12 +2369,10 @@ rel_ind:
return -EINVAL;
}
- /* store value, mask and history */
+ /* store value, mask and clear history */
rr->chan_req_val = chan_req_val;
rr->chan_req_mask = chan_req_mask;
- rr->cr_hist[2].valid = 0;
- rr->cr_hist[1].valid = 0;
- rr->cr_hist[0].valid = 0;
+ memset(rr->cr_hist, 0, sizeof(rr->cr_hist));
/* store establishment cause, so 'choose cell' selects the last cell
* after location updating */
@@ -1520,7 +2402,10 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
uint8_t tx_power;
enum gsm_band band;
- gsm_arfcn2band_rc(cs->arfcn, &band);
+ if (gsm_arfcn2band_rc(cs->arfcn, &band) != 0) {
+ LOGP(DRR, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n");
+ return -EINVAL;
+ }
/* already assigned */
if (rr->wait_assign == 2)
@@ -1538,10 +2423,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
}
/* shift history and store */
- memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]),
- sizeof(struct gsm48_cr_hist));
- memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]),
- sizeof(struct gsm48_cr_hist));
+ memmove(rr->cr_hist + 1, rr->cr_hist, sizeof(rr->cr_hist) - sizeof(rr->cr_hist[0]));
rr->cr_hist[0].valid = 1;
rr->cr_hist[0].ref.ra = rr->cr_ra;
rr->cr_hist[0].ref.t1 = ref->t1;
@@ -1709,32 +2591,31 @@ static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type)
&& s->si5
&& (!s->nb_ext_ind_si5 || s->si5bis)) {
struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas;
- int n = 0, i, refer_pcs;
+ int i;
+ bool refer_pcs;
+ int16_t arfcn;
LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n",
s->nb_ba_ind_si5);
rrmeas->nc_num = 0;
refer_pcs = gsm_refer_pcs(cs->arfcn, s);
- /* collect channels from freq list (1..1023,0) */
- for (i = 1; i <= 1024; i++) {
- if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) {
- if (n == 32) {
- LOGP(DRR, LOGL_NOTICE, "SI5* report "
- "exceeds 32 BCCHs\n");
- break;
- }
- if (refer_pcs && i >= 512 && i <= 810)
- rrmeas->nc_arfcn[n] = i | ARFCN_PCS;
- else
- rrmeas->nc_arfcn[n] = i & 1023;
- rrmeas->nc_rxlev_dbm[n] = -128;
- LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %s\n",
- gsm_print_arfcn(rrmeas->nc_arfcn[n]));
- n++;
- }
+ /* Collect channels from freq list in correct order. */
+ for (i = 0; i < 32; i++) {
+ arfcn = arfcn_from_freq_index(s, i);
+ if (arfcn < 0)
+ break;
+ if (refer_pcs && arfcn >= 512 && arfcn <= 810)
+ rrmeas->nc_arfcn[i] = arfcn | ARFCN_PCS;
+ else
+ rrmeas->nc_arfcn[i] = arfcn;
+ rrmeas->nc_rxlev_dbm[i] = -128;
+ LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis report arfcn %s (index %d)\n",
+ gsm_print_arfcn(rrmeas->nc_arfcn[i]), i);
}
- rrmeas->nc_num = n;
+ rrmeas->nc_num = i;
+ if (i == 32 && arfcn_from_freq_index(s, i) >= 0)
+ LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis/SI5ter define more than 32 channels.\n");
}
/* send sysinfo event to other layers */
@@ -1779,7 +2660,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg))))
+ if (!memcmp(si, s->si1_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si1_msg))))
return 0;
gsm48_decode_sysinfo1(s, si, msgb_l3len(msg));
@@ -1808,7 +2689,7 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg))))
+ if (!memcmp(si, s->si2_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2_msg))))
return 0;
gsm48_decode_sysinfo2(s, si, msgb_l3len(msg));
@@ -1837,7 +2718,7 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg), sizeof(s->si2b_msg))))
+ if (!memcmp(si, s->si2b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2b_msg))))
return 0;
gsm48_decode_sysinfo2bis(s, si, msgb_l3len(msg));
@@ -1866,7 +2747,7 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg), sizeof(s->si2t_msg))))
+ if (!memcmp(si, s->si2t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2t_msg))))
return 0;
gsm48_decode_sysinfo2ter(s, si, msgb_l3len(msg));
@@ -1896,7 +2777,7 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg))))
+ if (!memcmp(si, s->si3_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si3_msg))))
return 0;
gsm48_decode_sysinfo3(s, si, msgb_l3len(msg));
@@ -1931,14 +2812,12 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg))))
+ if (!memcmp(si, s->si4_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si4_msg))))
return 0;
gsm48_decode_sysinfo4(s, si, msgb_l3len(msg));
- LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s "
- "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc), s->lac);
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (lai=%s)\n", osmo_lai_name(&s->lai));
return gsm48_new_sysinfo(ms, si->header.system_information);
}
@@ -1946,9 +2825,10 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
/* receive "SYSTEM INFORMATION 5" message (9.1.37) */
static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
{
- struct gsm48_system_information_type_5 *si = msgb_l3(msg);
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1;
struct gsm48_sysinfo *s = ms->cellsel.si;
- int payload_len = msgb_l3len(msg) - sizeof(*si);
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
if (!s) {
LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 "
@@ -1962,7 +2842,7 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg))))
+ if (!memcmp(si, s->si5_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5_msg))))
return 0;
gsm48_decode_sysinfo5(s, si, msgb_l3len(msg));
@@ -1975,9 +2855,10 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
/* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
{
- struct gsm48_system_information_type_5bis *si = msgb_l3(msg);
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1;
struct gsm48_sysinfo *s = ms->cellsel.si;
- int payload_len = msgb_l3len(msg) - sizeof(*si);
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
if (!s) {
LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis"
@@ -1991,7 +2872,7 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg),
+ if (!memcmp(si, s->si5b_msg, OSMO_MIN(msgb_l3len(msg),
sizeof(s->si5b_msg))))
return 0;
@@ -2005,9 +2886,10 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
/* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
{
- struct gsm48_system_information_type_5ter *si = msgb_l3(msg);
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1;
struct gsm48_sysinfo *s = ms->cellsel.si;
- int payload_len = msgb_l3len(msg) - sizeof(*si);
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
if (!s) {
LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter"
@@ -2021,7 +2903,7 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg),
+ if (!memcmp(si, s->si5t_msg, OSMO_MIN(msgb_l3len(msg),
sizeof(s->si5t_msg))))
return 0;
@@ -2035,10 +2917,11 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
/* receive "SYSTEM INFORMATION 6" message (9.1.39) */
static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
{
- struct gsm48_system_information_type_6 *si = msgb_l3(msg);
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1;
struct gsm48_sysinfo *s = ms->cellsel.si;
struct rx_meas_stat *meas = &ms->meas;
- int payload_len = msgb_l3len(msg) - sizeof(*si);
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
if (!s) {
LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 "
@@ -2052,14 +2935,13 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
+ if (!memcmp(si, s->si6_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
return 0;
gsm48_decode_sysinfo6(s, si, msgb_l3len(msg));
- LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s "
- "lac 0x%04x SACCH-timeout %d)\n", gsm_print_mcc(s->mcc),
- gsm_print_mnc(s->mnc), s->lac, s->sacch_radio_link_timeout);
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (lai=%s SACCH-timeout %d)\n",
+ osmo_lai_name(&s->lai), s->sacch_radio_link_timeout);
meas->rl_fail = meas->s = s->sacch_radio_link_timeout;
LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail);
@@ -2067,6 +2949,72 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_new_sysinfo(ms, si->system_information);
}
+/* Receive "SYSTEM INFORMATION 10" message (9.1.50). */
+static int gsm48_rr_rx_sysinfo_10(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* NOTE: Short L2 header is included in this structure */
+ struct gsm48_system_information_type_10 *si = msgb_l3(msg);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 10 ignored.\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 20) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 10 message.\n");
+ return -EINVAL;
+ }
+
+ /* No complete SI5, cannot decode yet. */
+ if (!s->si5 || !(s->si5bis || !s->nb_ext_ind_si5))
+ return 0;
+
+ /* We decode when changed or when SI10 could not decoded, due to missing neighbor cell infos. */
+ if (!memcmp(si, s->si10_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si10_msg))) && s->si10)
+ return 0;
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 10\n");
+
+ gsm48_decode_sysinfo10(s, si, msgb_l3len(msg));
+
+ /* We cannot call gsm48_new_sysinfo, because it requires regular message types. */
+ return 0;
+}
+
+/* receive "SYSTEM INFORMATION 13" message (9.1.43a) */
+static int gsm48_rr_rx_sysinfo13(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_system_information_type_13 *si = msgb_l3(msg);
+ int rest_octets_len = msgb_l3len(msg) - sizeof(si->header);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO,
+ "No cell selected, SYSTEM INFORMATION 13 ignored\n");
+ return -EINVAL;
+ }
+
+ if (rest_octets_len < 0) {
+ LOGP(DRR, LOGL_NOTICE,
+ "Short read of SYSTEM INFORMATION 13 message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si13_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
+ return 0;
+
+ gsm48_decode_sysinfo13(s, si, msgb_l3len(msg));
+
+ LOGP(DRR, LOGL_INFO,
+ "New SYSTEM INFORMATION 13 (%s, RAC 0x%02x, NCO %u, MNO %u)\n",
+ s->gprs.egprs_supported ? "EGPRS" : "GPRS only",
+ s->gprs.rac, s->gprs.nco, s->gprs.nmo);
+
+ return gsm48_new_sysinfo(ms, si->header.system_information);
+}
+
/*
* paging
*/
@@ -2080,45 +3028,37 @@ static int gsm48_rr_chan2cause[4] = {
};
/* given LV of mobile identity is checked against ms */
-static uint8_t gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
+static uint8_t gsm_match_mi(struct osmocom_ms *ms, const uint8_t *mi_lv)
{
struct gsm322_cellsel *cs = &ms->cellsel;
- char imsi[16];
- uint32_t tmsi;
- uint8_t mi_type;
+ struct osmo_mobile_identity mi;
+ char buf[32];
+ int rc;
- if (mi[0] < 1)
- return 0;
- mi_type = mi[1] & GSM_MI_TYPE_MASK;
- switch (mi_type) {
+ rc = osmo_mobile_identity_decode(&mi, mi_lv+1, mi_lv[0], false);
+ if (rc < 0)
+ return rc;
+ osmo_mobile_identity_to_str_buf(buf, sizeof(buf), &mi);
+
+ switch (mi.type) {
case GSM_MI_TYPE_TMSI:
- if (mi[0] < 5)
- return 0;
- memcpy(&tmsi, mi+2, 4);
- if (ms->subscr.tmsi == ntohl(tmsi)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
- LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n",
- ntohl(tmsi));
-
- return mi_type;
+ if ((ms->subscr.tmsi == mi.tmsi)
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
+ LOGP(DPAG, LOGL_INFO, " %s matches\n", buf);
+ return mi.type;
} else
- LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
- ntohl(tmsi));
+ LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf);
break;
case GSM_MI_TYPE_IMSI:
- gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]);
- if (!strcmp(imsi, ms->subscr.imsi)) {
- LOGP(DPAG, LOGL_INFO, " IMSI %s matches\n", imsi);
-
- return mi_type;
+ if (!strcmp(mi.imsi, ms->subscr.imsi)) {
+ LOGP(DPAG, LOGL_INFO, " %s matches\n", buf);
+ return mi.type;
} else
- LOGP(DPAG, LOGL_INFO, " IMSI %s (not for us)\n", imsi);
+ LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf);
break;
default:
LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n",
- mi_type);
+ mi.type);
}
return 0;
@@ -2217,9 +3157,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
chan_2 = pa->cneed2;
/* first MI */
if (ms->subscr.tmsi == ntohl(pa->tmsi1)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1));
return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1,
GSM_MI_TYPE_TMSI);
@@ -2228,9 +3166,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
ntohl(pa->tmsi1));
/* second MI */
if (ms->subscr.tmsi == ntohl(pa->tmsi2)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2));
return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1,
GSM_MI_TYPE_TMSI);
@@ -2287,9 +3223,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
chan_4 = pa->cneed4;
/* first MI */
if (ms->subscr.tmsi == ntohl(pa->tmsi1)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1));
return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1,
GSM_MI_TYPE_TMSI);
@@ -2298,9 +3232,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
ntohl(pa->tmsi1));
/* second MI */
if (ms->subscr.tmsi == ntohl(pa->tmsi2)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2));
return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1,
GSM_MI_TYPE_TMSI);
@@ -2309,9 +3241,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
ntohl(pa->tmsi2));
/* third MI */
if (ms->subscr.tmsi == ntohl(pa->tmsi3)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3));
return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1,
GSM_MI_TYPE_TMSI);
@@ -2320,9 +3250,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
ntohl(pa->tmsi3));
/* fourth MI */
if (ms->subscr.tmsi == ntohl(pa->tmsi4)
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac) {
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) {
LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4));
return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1,
GSM_MI_TYPE_TMSI);
@@ -2338,23 +3266,22 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
*/
/* match request reference against request history */
-static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref)
+static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
int i;
uint8_t ia_t1, ia_t2, ia_t3;
uint8_t cr_t1, cr_t2, cr_t3;
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < hist_num; i++) {
/* filter confirmed RACH requests only */
if (rr->cr_hist[i].valid && ref->ra == rr->cr_hist[i].ref.ra) {
- ia_t1 = ref->t1;
- ia_t2 = ref->t2;
- ia_t3 = (ref->t3_high << 3) | ref->t3_low;
- ref = &rr->cr_hist[i].ref;
- cr_t1 = ref->t1;
- cr_t2 = ref->t2;
- cr_t3 = (ref->t3_high << 3) | ref->t3_low;
+ ia_t1 = ref->t1;
+ ia_t2 = ref->t2;
+ ia_t3 = (ref->t3_high << 3) | ref->t3_low;
+ cr_t1 = rr->cr_hist[i].ref.t1;
+ cr_t2 = rr->cr_hist[i].ref.t2;
+ cr_t3 = (rr->cr_hist[i].ref.t3_high << 3) | rr->cr_hist[i].ref.t3_low;
if (ia_t1 == cr_t1 && ia_t2 == cr_t2
&& ia_t3 == cr_t3) {
LOGP(DRR, LOGL_INFO, "request %02x matches "
@@ -2425,7 +3352,12 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
/* decode channel description */
LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n");
cd.chan_nr = ia->chan_desc.chan_nr;
- rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd.chan_nr);
+ return -EINVAL;
+ }
if (ia->chan_desc.h0.h) {
cd.h = 1;
gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio,
@@ -2439,8 +3371,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
} else {
cd.h = 0;
gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cd.arfcn |= ARFCN_PCS;
+ cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn);
LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x "
"ARFCN %s TS %u SS %u TSC %u)\n",
ia->timing_advance,
@@ -2461,7 +3392,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
}
/* request ref */
- if (gsm48_match_ra(ms, &ia->req_ref)) {
+ if (gsm48_match_ra(ms, &ia->req_ref, IMM_ASS_HISTORY)) {
/* channel description */
memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
/* timing advance */
@@ -2539,7 +3470,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
/* decode channel description */
LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n");
cd1.chan_nr = ia->chan_desc1.chan_nr;
- rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd1.chan_nr);
+ return -EINVAL;
+ }
if (ia->chan_desc1.h0.h) {
cd1.h = 1;
gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio,
@@ -2553,8 +3489,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
} else {
cd1.h = 0;
gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cd1.arfcn |= ARFCN_PCS;
+ cd1.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd1.arfcn);
LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x "
"chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
ia->timing_advance1,
@@ -2563,7 +3498,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
gsm_print_arfcn(cd1.arfcn), ch_ts, ch_subch, cd1.tsc);
}
cd2.chan_nr = ia->chan_desc2.chan_nr;
- rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd2.chan_nr);
+ return -EINVAL;
+ }
if (ia->chan_desc2.h0.h) {
cd2.h = 1;
gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio,
@@ -2577,8 +3517,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
} else {
cd2.h = 0;
gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cd2.arfcn |= ARFCN_PCS;
+ cd2.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd2.arfcn);
LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x "
"chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
ia->timing_advance2,
@@ -2599,7 +3538,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
}
/* request ref 1 */
- if (gsm48_match_ra(ms, &ia->req_ref1)) {
+ if (gsm48_match_ra(ms, &ia->req_ref1, IMM_ASS_HISTORY)) {
/* channel description */
memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now));
/* timing advance */
@@ -2615,7 +3554,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_rr_dl_est(ms);
}
/* request ref 2 */
- if (gsm48_match_ra(ms, &ia->req_ref2)) {
+ if (gsm48_match_ra(ms, &ia->req_ref2, IMM_ASS_HISTORY)) {
/* channel description */
memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now));
/* timing advance */
@@ -2665,7 +3604,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
(((uint8_t *)&ia->req_ref1) + i * 4);
LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT "
"(ref 0x%02x)\n", req_ref->ra);
- if (gsm48_match_ra(ms, req_ref)) {
+ if (gsm48_match_ra(ms, req_ref, IMM_ASS_HISTORY)) {
/* wait indication */
t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4);
if (t3122_value)
@@ -2694,12 +3633,13 @@ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
struct tlv_parsed tp;
if (payload_len < 0) {
- LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT "
- "message.\n");
- return gsm48_rr_tx_rr_status(ms,
- GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT message\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0) < 0) {
+ LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
- tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0);
return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
@@ -2753,7 +3693,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
if (rep_valid) {
int8_t strongest, current;
uint8_t ncc;
- int i, index;
+ int i, index, strongest_i;
#if 0
/* FIXME: multi-band reporting, if not: 0 = normal reporting */
@@ -2767,13 +3707,20 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
current = -128; /* -infinite */
index = 0;
for (i = 0; i < rrmeas->nc_num; i++) {
+ /* Skip stronger cells that have been added to measurement report so far. */
+ if (rrmeas->nc_rxlev_dbm[i] > strongest)
+ continue;
+ /* Skip cells with equal strength that have been added so far. */
+ if (rrmeas->nc_rxlev_dbm[i] == strongest && i <= strongest_i)
+ continue;
/* only check if NCC is permitted */
ncc = rrmeas->nc_bsic[i] >> 3;
if ((s->nb_ncc_permitted_si6 & (1 << ncc))
- && rrmeas->nc_rxlev_dbm[i] > current
- && rrmeas->nc_rxlev_dbm[i] < strongest) {
+ && rrmeas->nc_rxlev_dbm[i] > current) {
current = rrmeas->nc_rxlev_dbm[i];
+ strongest = current;
index = i;
+ strongest_i = i;
}
}
if (current == -128) /* no more found */
@@ -2878,6 +3825,9 @@ int gsm48_rr_los(struct osmocom_ms *ms)
LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n");
+ if (rr->vgcs.group_state != GSM48_RR_GST_OFF)
+ return gsm48_rr_group_rel(ms, RR_REL_CAUSE_LOST_SIGNAL);
+
/* stop T3211 if running */
stop_rr_t3110(rr);
@@ -2982,18 +3932,22 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms,
gsm48_rr_tx_meas_rep(ms);
/* establish */
- LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n");
- rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts);
- LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, "
- "audio-mode %d, cipher %d\n", ch_type, ch_subch, ch_ts,
- cd->mode, rr->audio_mode, rr->cipher_type + 1);
+ LOGP(DRR, LOGL_INFO, "establishing channel in dedicated/group mode\n");
+
+ if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd->chan_nr);
+ return -EINVAL;
+ }
+
+ LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, audio-mode %d, flags 0x%02x, cipher %d\n",
+ ch_type, ch_subch, ch_ts, cd->mode, rr->audio_mode, cd->tch_flags, rr->cipher_type + 1);
if (cd->h)
- l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn,
- ma, ma_len, cd->chan_nr, cd->tsc, cd->mode,
- rr->audio_mode);
+ l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, ma, ma_len, cd->chan_nr, cd->tsc, cd->mode,
+ rr->audio_mode, cd->tch_flags);
else
- l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc,
- cd->mode, rr->audio_mode);
+ l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, cd->mode, rr->audio_mode, cd->tch_flags);
rr->dm_est = 1;
/* old SI 5/6 are not valid on a new dedicated channel */
@@ -3023,7 +3977,7 @@ static int gsm48_rr_channel_after_time(struct osmocom_ms *ms,
l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr,
rr->cipher_type + 1, subscr->key, 8);
- gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode);
+ gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode, cd->tch_flags);
return 0;
}
@@ -3035,8 +3989,8 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm48_sysinfo *s = cs->si;
struct gsm_settings *set = &ms->settings;
- int i, pcs, index;
- uint16_t arfcn;
+ int i, index;
+ uint16_t arfcn, pcs;
pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0;
@@ -3139,7 +4093,9 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
/* convert to band_arfcn and check for unsported frequency */
for (i = 0; i < *ma_len; i++) {
- arfcn = ma[i] | pcs;
+ arfcn = ma[i];
+ if (arfcn >= 512 && arfcn <= 810)
+ arfcn |= pcs;
ma[i] = arfcn;
index = arfcn2index(arfcn);
if (!(set->freq_map[index >> 3] & (1 << (index & 7)))) {
@@ -3156,12 +4112,11 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
static int gsm48_rr_dl_est(struct osmocom_ms *ms)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
- struct gsm322_cellsel *cs = &ms->cellsel;
struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_cellsel *cs = &ms->cellsel;
struct msgb *nmsg;
struct gsm48_hdr *gh;
struct gsm48_pag_rsp *pr;
- uint8_t mi[11];
uint16_t ma[64];
uint8_t ma_len;
@@ -3235,26 +4190,21 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms)
pr->cm2_len = sizeof(pr->cm2);
gsm48_rr_enc_cm2(ms, &pr->cm2, rr->cd_now.arfcn);
/* mobile identity */
- if (ms->subscr.tmsi != 0xffffffff
- && ms->subscr.mcc == cs->sel_mcc
- && ms->subscr.mnc == cs->sel_mnc
- && ms->subscr.lac == cs->sel_lac
+ if (ms->subscr.tmsi != GSM_RESERVED_TMSI
+ && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)
&& rr->paging_mi_type == GSM_MI_TYPE_TMSI) {
- gsm48_generate_mid_from_tmsi(mi, subscr->tmsi);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false);
LOGP(DRR, LOGL_INFO, "sending paging response with "
"TMSI\n");
} else if (subscr->imsi[0]) {
- gsm48_generate_mid_from_imsi(mi, subscr->imsi);
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false);
LOGP(DRR, LOGL_INFO, "sending paging response with "
"IMSI\n");
} else {
- mi[1] = 1;
- mi[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false);
LOGP(DRR, LOGL_INFO, "sending paging response without "
"TMSI/IMSI\n");
}
- msgb_put(nmsg, 1 + mi[1]);
- memcpy(pr->data, mi + 1, 1 + mi[1]);
}
#ifdef TEST_FREQUENCY_MOD
@@ -3328,6 +4278,12 @@ static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
struct msgb *nmsg;
struct gsm48_rr_hdr *nrrh;
+ /* Handle on group channel. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
+ LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release indication.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL);
+ }
+
/* switch back to old channel, if modify/ho failed */
switch (rr->modify_state) {
case GSM48_RR_MOD_ASSIGN:
@@ -3374,12 +4330,13 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
uint8_t *mode;
if (payload_len < 0) {
- LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE "
- "message.\n");
- return gsm48_rr_tx_rr_status(ms,
- GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) {
+ LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
- tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0);
LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x\n",
cr->rr_cause);
@@ -3397,6 +4354,9 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+ /* When we receive a channel release, we are not just releasing the transmit mode. */
+ rr->vgcs.group_state = GSM48_RR_GST_OFF;
+
/* start T3110, so that two DISCs can be sent due to T200 timeout */
start_rr_t3110(rr, 1, 500000);
@@ -3414,27 +4374,59 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
+/* Release of channel on VGCS/VBS. */
+static int gsm48_rr_rx_chan_rel_ui(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr);
+ struct tlv_parsed tp;
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) {
+ LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+
+ LOGP(DRR, LOGL_INFO, "CHANNEL RELESE via UI frame with cause 0x%02x\n", cr->rr_cause);
+
+ /* Only allow when in group receive mode. */
+ if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE)
+ return 0;
+
+ return gsm48_rr_group_rel(ms, RR_REL_CAUSE_NORMAL);
+}
+
/*
* frequency redefition, channel mode modify, assignment, and handover
*/
/* set channel mode in case of TCH */
-static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
- uint8_t mode)
+static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
uint8_t ch_type, ch_subch, ch_ts;
+ if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, chan_nr);
+ return -EINVAL;
+ }
+
/* only apply mode to TCH/F or TCH/H */
- rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts);
if (ch_type != RSL_CHAN_Bm_ACCHs
&& ch_type != RSL_CHAN_Lm_ACCHs)
return -ENOTSUP;
- /* setting (new) timing advance */
- LOGP(DRR, LOGL_INFO, "setting TCH mode to %d, audio mode to %d\n",
- mode, rr->audio_mode);
- l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, rr->tch_loop_mode);
+ /* Apply indicated channel mode */
+ LOGP(DRR, LOGL_INFO, "setting TCH mode to %s, audio mode to %d, tch flags to %d\n",
+ get_value_string(gsm48_chan_mode_names, mode), rr->audio_mode, tch_flags);
+ l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, tch_flags, rr->tch_loop_mode);
return 0;
}
@@ -3471,7 +4463,12 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
/* decode channel description */
LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n");
cd.chan_nr = fr->chan_desc.chan_nr;
- rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd.chan_nr);
+ return -EINVAL;
+ }
if (fr->chan_desc.h0.h) {
cd.h = 1;
gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio,
@@ -3481,8 +4478,7 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg)
} else {
cd.h = 0;
gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cd.arfcn |= ARFCN_PCS;
+ cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn);
LOGP(DRR, LOGL_INFO, " (ARFCN %s TS %u SS %u TSC %u)\n",
gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
}
@@ -3561,7 +4557,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
struct gsm48_rr_cd *cd = &rr->cd_now;
uint8_t ch_type, ch_subch, ch_ts;
- uint8_t cause;
+ int rc;
LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n");
@@ -3575,7 +4571,12 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
/* decode channel description */
cd->chan_nr = cm->chan_desc.chan_nr;
- rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd->chan_nr);
+ return -EINVAL;
+ }
if (cm->chan_desc.h0.h) {
cd->h = 1;
gsm48_decode_chan_h1(&cm->chan_desc, &cd->tsc, &cd->maio,
@@ -3586,21 +4587,36 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg)
} else {
cd->h = 0;
gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cd->arfcn |= ARFCN_PCS;
+ cd->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd->arfcn);
LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u "
"TSC %u mode %u)\n", cm->chan_desc.chan_nr,
gsm_print_arfcn(cd->arfcn), ch_ts, ch_subch, cd->tsc,
cm->mode);
}
- /* mode */
- cause = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode);
- if (cause)
- return gsm48_rr_tx_rr_status(ms, cause);
+
+ /**
+ * According to 3GPP TS 04.08, section 3.4.6.1.3
+ * "Abnormal cases" of "channel mode modify procedure",
+ * if the MS doesn't support the indicated channel mode,
+ * it shall retain the old mode and return the associated
+ * channel mode information in the ACKNOWLEDGE message.
+ */
+
+ /* Check if we support this channel mode */
+ rc = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode);
+ if (rc)
+ goto ack;
+
+ /* Attempt to apply this mode */
+ rc = gsm48_rr_set_mode(ms, cd->chan_nr, cm->mode, cd->tch_flags);
+ if (rc)
+ goto ack;
+
+ /* Finally set (a new) mode */
cd->mode = cm->mode;
- gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode);
- return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cm->mode);
+ack:
+ return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cd->mode);
}
/* 9.1.3 sending ASSIGNMENT COMPLETE */
@@ -3686,19 +4702,25 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
cdb->ind_tx_power = rr->cd_now.ind_tx_power;
if (payload_len < 0) {
- LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND "
- "message.\n");
- return gsm48_rr_tx_rr_status(ms,
- GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0) < 0) {
+ LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
- tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0);
/* decode channel description (before time) */
if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) {
struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
cdb->chan_nr = ccd->chan_nr;
- rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cdb->chan_nr);
+ return -EINVAL;
+ }
if (ccd->h0.h) {
cdb->h = 1;
gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
@@ -3709,8 +4731,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
} else {
cdb->h = 0;
gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cdb->arfcn |= ARFCN_PCS;
+ cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn);
LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
"ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr,
gsm_print_arfcn(cdb->arfcn),
@@ -3721,7 +4742,12 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
/* decode channel description (after time) */
cda->chan_nr = ac->chan_desc.chan_nr;
- rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cda->chan_nr);
+ return -EINVAL;
+ }
if (ac->chan_desc.h0.h) {
cda->h = 1;
gsm48_decode_chan_h1(&ac->chan_desc, &cda->tsc, &cda->maio,
@@ -3732,8 +4758,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
} else {
cda->h = 0;
gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cda->arfcn |= ARFCN_PCS;
+ cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn);
LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u "
"SS %u TSC %u)\n", ac->chan_desc.chan_nr,
gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc);
@@ -4059,10 +5084,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
cdb->ind_tx_power = rr->cd_now.ind_tx_power;
if (payload_len < 0) {
- LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND "
- "message.\n");
- return gsm48_rr_tx_rr_status(ms,
- GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0) < 0) {
+ LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
}
/* cell description */
@@ -4072,8 +5099,6 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
rr->chan_req_val = ho->ho_ref;
rr->chan_req_mask = 0x00;
- tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
-
/* sync ind */
if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) {
gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *)
@@ -4087,7 +5112,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *)
TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE);
cdb->chan_nr = ccd->chan_nr;
- rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cdb->chan_nr);
+ return -EINVAL;
+ }
if (ccd->h0.h) {
cdb->h = 1;
gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio,
@@ -4098,8 +5128,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
} else {
cdb->h = 0;
gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cdb->arfcn |= ARFCN_PCS;
+ cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn);
LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x "
"ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr,
gsm_print_arfcn(cdb->arfcn),
@@ -4110,7 +5139,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
/* decode channel description (after time) */
cda->chan_nr = ho->chan_desc.chan_nr;
- rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cda->chan_nr);
+ return -EINVAL;
+ }
if (ho->chan_desc.h0.h) {
cda->h = 1;
gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio,
@@ -4121,8 +5155,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
} else {
cda->h = 0;
gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn);
- if (gsm_refer_pcs(cs->arfcn, s))
- cda->arfcn |= ARFCN_PCS;
+ cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn);
LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u "
"SS %u TSC %u)\n", ho->chan_desc.chan_nr,
gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc);
@@ -4368,6 +5401,16 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
+/* send HANDOVER/UPLINK ACCESS burst (9.1.14) */
+static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ LOGP(DRR, LOGL_INFO, "send access burst on DCCH (ref 0x%02x)\n", ref);
+
+ return l1ctl_tx_rach_req(ms, rr->cd_now.chan_nr, 0x00, ref, 0, offset, uic);
+}
+
/* send all queued messages down to layer 2 */
static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
{
@@ -4390,17 +5433,27 @@ static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
return 0;
}
-/* channel is resumed in dedicated mode */
+/* Channel is resumed in dedicated mode or uplink is estabished. */
static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
- LOGP(DRR, LOGL_INFO, "data link is resumed\n");
+ if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
+ LOGP(DRR, LOGL_INFO, "data link established on uplink\n");
- /* transmit queued frames during ho / ass transition */
- gsm48_rr_dequeue_down(ms);
+ /* Confirm uplink access. */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_CNF);
+ if (nmsg)
+ gsm48_rr_upmsg(ms, nmsg);
+ } else {
+ LOGP(DRR, LOGL_INFO, "data link is resumed\n");
- rr->modify_state = GSM48_RR_MOD_NONE;
+ /* transmit queued frames during ho / ass transition */
+ gsm48_rr_dequeue_down(ms);
+
+ rr->modify_state = GSM48_RR_MOD_NONE;
+ }
return 0;
}
@@ -4490,6 +5543,13 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
struct gsm48_rr_hdr *nrrh;
uint16_t acc_class;
+ /* Reject during group call. */
+ if (rr->vgcs.group_state != GSM48_RR_GST_OFF) {
+ LOGP(DRR, LOGL_INFO, "We have a group call, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
/* 3.3.1.1.3.2 */
if (osmo_timer_pending(&rr->t3122)) {
if (rrh->cause != RR_EST_CAUSE_EMERGENCY) {
@@ -4577,10 +5637,317 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)),
msgb_l3(msg), msgb_l3len(msg));
+ if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
+ /* Release group receive mode. */
+ l1ctl_tx_dm_rel_req(rr->ms);
+ rr->ms->meas.rl_fail = 0;
+ l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED);
+ }
+
/* request channel */
return gsm48_rr_chan_req(ms, rrh->cause, 0, 0);
}
+/* 3.3.3.2 Request for group receive mode. */
+static int gsm48_rr_group_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = &cs->sel_si;
+ struct gsm48_chan_desc *ch_desc = (struct gsm48_chan_desc *)(msg->data + sizeof(struct gsm48_rr_hdr));
+ uint8_t cause;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+ struct gsm48_rr_cd cd;
+ uint8_t ch_type, ch_subch, ch_ts;
+ uint16_t ma[64];
+ uint8_t ma_len;
+ int rc;
+
+ /* if state is not idle */
+ if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_OFF) {
+ LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+reject:
+ LOGP(DSUM, LOGL_INFO, "Joining group call channel not possible\n");
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = cause;
+ return gsm48_rr_upmsg(ms, nmsg);
+ }
+
+ /* cell selected */
+ if (!cs->selected) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
+ /* check if camping */
+ if (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && cs->state != GSM322_C7_CAMPED_ANY_CELL) {
+ LOGP(DRR, LOGL_INFO, "Not camping, rejecting! "
+ "(cs->state = %d)\n", cs->state);
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
+ /* get channel description */
+ memset(&cd, 0, sizeof(cd));
+ cd.chan_nr = ch_desc->chan_nr;
+ if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRR, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, cd.chan_nr);
+ cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ goto reject;
+ }
+ if (ch_desc->h0.h) {
+ LOGP(DRR, LOGL_ERROR, "HOPPING NOT SUPPORTED, PLEASE FIX!\n");
+ cd.h = 1;
+ gsm48_decode_chan_h1(ch_desc, &cd.tsc, &cd.maio,
+ &cd.hsn);
+ LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n",
+ ch_desc->chan_nr, cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc);
+ } else {
+ cd.h = 0;
+ gsm48_decode_chan_h0(ch_desc, &cd.tsc, &cd.arfcn);
+ cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn);
+ LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n",
+ ch_desc->chan_nr, gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc);
+ }
+ if (gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len)) {
+ cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ goto reject;
+ }
+
+ /* Turn off transmitter. */
+ cd.tch_flags |= L1CTL_TCH_FLAG_RXONLY;
+
+ /* Set mode to Speech V1. FIXME: Add AMR support. */
+ cd.mode = GSM48_CMODE_SPEECH_V1;
+
+ /* Set current channel and also store as 'vgcs.cd_group', used when leaving dedicated mode. */
+ memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
+ memcpy(&rr->vgcs.cd_group, &cd, sizeof(rr->vgcs.cd_group));
+
+ /* tell cell selection process to leave idle mode
+ * NOTE: this must be sent unbuffered, because the state may not
+ * change until idle mode is left
+ */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE);
+ if (!nmsg)
+ return -ENOMEM;
+ rc = gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+ if (rc) {
+ LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n");
+ cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ goto reject;
+ }
+
+ /* Initial uplink state. */
+ rr->vgcs.uplink_free = false;
+
+ /* Set group state to receive mode. */
+ rr->vgcs.group_state = GSM48_RR_GST_RECEIVE;
+
+ /* Wait until synced to the CCCH ... */
+ return 0;
+}
+/* ... Continue after synced to the CCCH. */
+static int gsm48_rr_group_req_continue(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ uint16_t ma[64];
+ uint8_t ma_len;
+ struct msgb *nmsg;
+
+ /* get hopping sequence, if required */
+ gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len);
+ /* activate channel */
+ gsm48_rr_activate_channel(ms, &rr->vgcs.cd_group, ma, ma_len);
+
+ /* Confirm group call channel. */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_CNF);
+ if (!nmsg)
+ return -ENOMEM;
+ return gsm48_rr_upmsg(ms, nmsg);
+}
+
+/* After "loss of signal"/release in group transmit or receive mode, return IDLE and release towards MM. */
+static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+ uint8_t *mode;
+
+ /* Stop group receive and transmit timers. */
+ stop_rr_t_ul_free(rr);
+ stop_rr_t3128(rr);
+ stop_rr_t3130(rr);
+
+ if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) {
+ struct msgb *nmsg;
+
+ LOGP(DRR, LOGL_INFO, "Channel lost, send (local) release to layer 2.\n");
+
+ /* Go into group receive mode, so that the channel gets released on LAPD release confirm. */
+ rr->vgcs.group_state = GSM48_RR_GST_RECEIVE;
+ if (rr->state != GSM48_RR_ST_REL_PEND)
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* release message */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = RSL_REL_LOCAL_END;
+ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);
+
+ /* release SAPI 3 link, if exits */
+ gsm48_release_sapi3_link(ms);
+
+ /* Wait for LAPD to confirm. Then return idle. */
+ return 0;
+ }
+
+ /* Go back to IDLE mode. */
+ rr->vgcs.group_state = GSM48_RR_GST_OFF;
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+
+ /* Indicate release to MM. */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = cause;
+ return gsm48_rr_upmsg(ms, nmsg);
+}
+
+/* Release group channel, return to IDLE. */
+static int gsm48_rr_group_rel_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ /* Only in group receive/transmit mode. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_OFF)
+ return 0;
+
+ if (rr->state == GSM48_RR_ST_CONN_PEND || rr->state == GSM48_RR_ST_DEDICATED) {
+ LOGP(DRR, LOGL_INFO, "Connot release group channel yet, perform uplink release first.\n");
+ gsm48_rr_uplink_rel_req(ms, msg);
+ if (rr->state == GSM48_RR_ST_REL_PEND) {
+ /* Leave the group call state, so that we change to IDLE when we released the uplink. */
+ rr->vgcs.group_state = GSM48_RR_GST_OFF;
+ return 0;
+ }
+ }
+
+ /* Stop group receive and transmit timers. */
+ stop_rr_t_ul_free(rr);
+ stop_rr_t3128(rr);
+ stop_rr_t3130(rr);
+
+ /* Unset group state. Wait, if release is pending. */
+ rr->vgcs.group_state = GSM48_RR_GST_OFF;
+ if (rr->state != GSM48_RR_ST_IDLE) {
+ LOGP(DRR, LOGL_INFO, "Cannot release group channel yet, wait to return to IDLE state.\n");
+ return 0;
+ }
+
+ LOGP(DRR, LOGL_INFO, "Release Group channel.\n");
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+ return 0;
+}
+
+/* Request uplink in group receive mode. */
+static int gsm48_rr_uplink_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ /* Only in group receive mode */
+ if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) {
+ LOGP(DRR, LOGL_INFO, "We are not in group receive mode yet, rejecting!\n");
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = RR_REL_CAUSE_TRY_LATER;
+ return gsm48_rr_upmsg(ms, nmsg);
+ }
+
+ LOGP(DRR, LOGL_INFO, "Changing from group receive mode to group transmit mode.\n");
+
+ /* Enter uplink access procedure. */
+ new_rr_state(rr, GSM48_RR_ST_CONN_PEND);
+
+ /* Set group state to transmit mode. */
+ rr->vgcs.group_state = GSM48_RR_GST_TRANSMIT;
+
+ /* Uplink investigation procedure (3.3.1.2.1.1) */
+ if (!rr->vgcs.uplink_free) {
+ start_rr_t3128(rr, GSM_T3128_MS);
+ return 0;
+ }
+
+ rr->vgcs.uplink_tries = 3;
+ return gsm48_rr_uplink_access(ms, NULL);
+}
+
+/* Leave uplink, also called when leaving group channel. */
+static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm_settings *set = &ms->settings;
+ struct msgb *nmsg;
+ uint8_t *mode;
+
+ /* Only in group transmit mode */
+ if (rr->vgcs.group_state != GSM48_RR_GST_TRANSMIT)
+ return -EINVAL;
+
+ /* Stop group transmit mode timers. */
+ stop_rr_t3128(rr);
+ stop_rr_t3130(rr);
+
+ /* Continue if in dedicated mode, so we release and wait for uplink to become free. */
+ if (rr->state != GSM48_RR_ST_DEDICATED)
+ return 0;
+
+ LOGP(DRR, LOGL_INFO, "Returning from group transmit to group receive mode.\n");
+
+ /* Leave uplink, wait for uplink being free, channel release or channel/link failure. */
+ gsm48_rr_tx_uplink_release(ms, GSM48_RR_CAUSE_LEAVE_GROUP_CA);
+
+ /* Go into release pending mode. */
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* Special setting where we release locally. This means we wait for free uplink an then release. */
+ if (set->uplink_release_local) {
+ LOGP(DRR, LOGL_INFO, "Release L2 locally as specified via VTY. Wait for UPLINK FREE.\n");
+ return 0;
+ }
+
+ /* Release dedicated mode. */
+ /* disconnect the main signalling link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = RSL_REL_NORMAL;
+ gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0);
+
+ return 0;
+}
+
/* 3.4.2 transfer data in dedicated mode */
static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
{
@@ -4625,6 +5992,8 @@ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
/* 3.4.2 data from layer 2 to RR and upper layer*/
static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t sapi = rllh->link_id & 7;
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_rr_hdr *rrh;
uint8_t pdisc = gh->proto_discr & 0x0f;
@@ -4664,6 +6033,9 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
case GSM48_MT_RR_CHAN_REL:
rc = gsm48_rr_rx_chan_rel(ms, msg);
break;
+ case GSM48_MT_RR_UPLINK_RELEASE:
+ rc = gsm48_rr_rx_uplink_release(ms, msg);
+ break;
case GSM48_MT_RR_APP_INFO:
LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n");
break;
@@ -4690,6 +6062,7 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
msgb_push(msg, sizeof(struct gsm48_rr_hdr));
rrh = (struct gsm48_rr_hdr *)msg->data;
rrh->msg_type = GSM48_RR_DATA_IND;
+ rrh->sapi = sapi;
return gsm48_rr_upmsg(ms, msg);
}
@@ -4712,6 +6085,8 @@ static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_rr_rx_sysinfo3(ms, msg);
case GSM48_MT_RR_SYSINFO_4:
return gsm48_rr_rx_sysinfo4(ms, msg);
+ case GSM48_MT_RR_SYSINFO_13:
+ return gsm48_rr_rx_sysinfo13(ms, msg);
default:
#if 0
LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n",
@@ -4726,6 +6101,11 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+ if (msgb_l3len(msg) < sizeof(*sih)) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of CCCH message.\n");
+ return -EINVAL;
+ }
+
switch (sih->system_information) {
case GSM48_MT_RR_PAG_REQ_1:
return gsm48_rr_rx_pag_req_1(ms, msg);
@@ -4740,6 +6120,10 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_rr_rx_imm_ass_ext(ms, msg);
case GSM48_MT_RR_IMM_ASS_REJ:
return gsm48_rr_rx_imm_ass_rej(ms, msg);
+
+ case GSM48_MT_RR_NOTIF_NCH:
+ return gsm48_rr_rx_notif_nch(ms, msg);
+
default:
#if 0
LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n",
@@ -4749,48 +6133,70 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
}
}
+#define N201_Bter_SACCH 21
+#define N201_Bter_SDCCH_FACCH 23
+#define N201_B4 19
+
/* receive ACCH at RR layer */
static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
{
- struct gsm48_rrlayer *rr = &ms->rrlayer;
- struct gsm_settings *set = &ms->settings;
- struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
- struct gsm48_hdr *sih = msgb_l3(msg);
- uint8_t ind_ta, ind_tx_power;
-
- if (msgb_l2len(msg) < sizeof(*rllh) + 2 + 2) {
- LOGP(DRR, LOGL_ERROR, "Missing TA and TX_POWER IEs\n");
- return -EINVAL;
+ const struct gsm48_system_information_type_header *sih;
+ const struct gsm48_hdr_sh *sgh;
+ const struct gsm48_hdr *gh;
+
+ /* Bter frame (SACCH or SDCCH/FACCH) */
+ if (msgb_l3len(msg) == N201_Bter_SACCH || msgb_l3len(msg) == N201_Bter_SDCCH_FACCH) {
+ sgh = msgb_l3(msg);
+ if (sgh->rr_short_pd != GSM48_PDISC_SH_RR) {
+ LOGP(DRR, LOGL_NOTICE, "Short header message is not an RR message.\n");
+ return -EINVAL;
+ }
+ switch (sgh->msg_type) {
+ case GSM48_MT_RR_SH_UL_FREE:
+ return gsm48_rr_rx_uplink_free(ms, msg);
+ case GSM48_MT_RR_SH_FACCH:
+ return gsm48_rr_rx_notif_facch(ms, msg);
+ case GSM48_MT_RR_SH_SI10:
+ return gsm48_rr_rx_sysinfo_10(ms, msg);
+ default:
+ LOGP(DRR, LOGL_NOTICE, "Short header message type 0x%02x unsupported.\n", sgh->msg_type);
+ return -EINVAL;
+ }
}
- ind_ta = rllh->data[1];
- ind_tx_power = rllh->data[3];
- LOGP(DRR, LOGL_INFO, "Indicated ta %d (actual ta %d)\n",
- ind_ta, ind_ta - set->alter_delay);
- LOGP(DRR, LOGL_INFO, "Indicated tx_power %d\n",
- ind_tx_power);
- if (ind_ta != rr->cd_now.ind_ta
- || ind_tx_power != rr->cd_now.ind_tx_power) {
- LOGP(DRR, LOGL_INFO, "setting new ta and tx_power\n");
- l1ctl_tx_param_req(ms, ind_ta - set->alter_delay,
- (set->alter_tx_power) ? set->alter_tx_power_value
- : ind_tx_power);
- rr->cd_now.ind_ta = ind_ta;
- rr->cd_now.ind_tx_power = ind_tx_power;
+ /* B4 frame (SACCH) */
+ if ((msg->cb[0] & 0x40) && msgb_l3len(msg) == N201_B4) {
+ sih = msgb_l3(msg);
+ switch (sih->system_information) {
+ case GSM48_MT_RR_SYSINFO_5:
+ return gsm48_rr_rx_sysinfo5(ms, msg);
+ case GSM48_MT_RR_SYSINFO_5bis:
+ return gsm48_rr_rx_sysinfo5bis(ms, msg);
+ case GSM48_MT_RR_SYSINFO_5ter:
+ return gsm48_rr_rx_sysinfo5ter(ms, msg);
+ case GSM48_MT_RR_SYSINFO_6:
+ return gsm48_rr_rx_sysinfo6(ms, msg);
+ default:
+ LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", sih->system_information);
+ return -EINVAL;
+ }
}
- switch (sih->msg_type) {
- case GSM48_MT_RR_SYSINFO_5:
- return gsm48_rr_rx_sysinfo5(ms, msg);
- case GSM48_MT_RR_SYSINFO_5bis:
- return gsm48_rr_rx_sysinfo5bis(ms, msg);
- case GSM48_MT_RR_SYSINFO_5ter:
- return gsm48_rr_rx_sysinfo5ter(ms, msg);
- case GSM48_MT_RR_SYSINFO_6:
- return gsm48_rr_rx_sysinfo6(ms, msg);
+ /* B frame (UI frame on VGCS) */
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOGP(DRR, LOGL_NOTICE, "ACCH message too short.\n");
+ return -EINVAL;
+ }
+ gh = msgb_l3(msg);
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_VGCS_UPL_GRANT:
+ return gsm48_rr_rx_vgcs_uplink_grant(ms, msg);
+ case GSM48_MT_RR_UPLINK_BUSY:
+ return gsm48_rr_rx_uplink_busy(ms, msg);
+ case GSM48_MT_RR_CHAN_REL:
+ return gsm48_rr_rx_chan_rel_ui(ms, msg);
default:
- LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n",
- sih->msg_type);
+ LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", gh->msg_type);
return -EINVAL;
}
}
@@ -4798,15 +6204,40 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
/* unit data from layer 2 to RR layer */
static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm_settings *set = &ms->settings;
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
struct tlv_parsed tv;
+ int ind_ta = -1, ind_tx_power = -1;
uint8_t ch_type, ch_subch, ch_ts;
DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
rllh->chan_nr, rllh->link_id);
- rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Update TX power and timing advance, if included in message. */
+ if (TLVP_PRES_LEN(&tv, RSL_IE_TIMING_ADVANCE, 1)) {
+ ind_ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE);
+ LOGP(DRR, LOGL_INFO, "DL SACCH indicates ta %d (actual ta %d)\n", ind_ta, ind_ta - set->alter_delay);
+ }
+ if (TLVP_PRES_LEN(&tv, RSL_IE_MS_POWER, 1)) {
+ ind_tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER);
+ LOGP(DRR, LOGL_INFO, "DL SACCH indicates tx_power %d\n", ind_tx_power);
+ }
+ if ((ind_ta >= 0 && ind_ta != rr->cd_now.ind_ta) ||
+ (ind_tx_power >= 0 && ind_tx_power != rr->cd_now.ind_tx_power)) {
+ LOGP(DRR, LOGL_INFO, "Applying new ta and tx_power\n");
+ l1ctl_tx_param_req(ms, ind_ta - set->alter_delay,
+ (set->alter_tx_power) ? set->alter_tx_power_value : ind_tx_power);
+ rr->cd_now.ind_ta = ind_ta;
+ rr->cd_now.ind_tx_power = ind_tx_power;
+ }
+
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
return -EIO;
@@ -4822,6 +6253,10 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DCS, LOGL_INFO, "Channel provides data.\n");
cs->ccch_state = GSM322_CCCH_ST_DATA;
+ /* in group receive mode */
+ if (ms->rrlayer.vgcs.group_state == GSM48_RR_GST_RECEIVE)
+ return gsm48_rr_group_req_continue(ms);
+
/* in dedicated mode */
if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
return gsm48_rr_tx_rand_acc(ms, NULL);
@@ -4840,7 +6275,13 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
// TODO: timer depends on BCCH config
}
- rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, rllh->chan_nr);
+ return -EINVAL;
+ }
+
switch (ch_type) {
case RSL_CHAN_PCH_AGCH:
return gsm48_rr_rx_pch_agch(ms, msg);
@@ -4850,6 +6291,7 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
case RSL_CHAN_Lm_ACCHs:
case RSL_CHAN_SDCCH4_ACCH:
case RSL_CHAN_SDCCH8_ACCH:
+ msg->cb[0] = rllh->link_id;
return gsm48_rr_rx_acch(ms, msg);
default:
LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
@@ -4908,6 +6350,12 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
uint16_t ma[64];
uint8_t ma_len;
+ /* Handle on group channel. */
+ if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
+ LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release confirm.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL);
+ }
+
/* switch back to old channel, if modify/ho failed */
switch (rr->modify_state) {
case GSM48_RR_MOD_ASSIGN:
@@ -4954,7 +6402,11 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
stop_rr_t3110(rr);
/* send release indication */
- nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) {
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND);
+ rr->vgcs.group_state = GSM48_RR_GST_OFF;
+ } else
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
if (!nmsg)
return -ENOMEM;
nrrh = (struct gsm48_rr_hdr *)nmsg->data;
@@ -5002,6 +6454,12 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
if (rr->modify_state)
return 0;
+ /* Handle on group channel. */
+ if ((link_id & 7) == 0 && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
+ LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link failure.\n");
+ return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE);
+ }
+
/* send abort ind to upper layer */
nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND);
if (!nmsg)
@@ -5164,7 +6622,13 @@ static int gsm48_rr_est_req_sapi3(struct osmocom_ms *ms, struct msgb *msg)
return gsm48_rr_upmsg(ms, nmsg);
}
- rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, rr->cd_now.chan_nr);
+ return -EINVAL;
+ }
+
if (ch_type != RSL_CHAN_Bm_ACCHs
&& ch_type != RSL_CHAN_Lm_ACCHs) {
LOGP(DRR, LOGL_INFO, "Requesting DCCH link, because no TCH "
@@ -5258,11 +6722,6 @@ static struct dldatastate {
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},
-#if 0
- {SBIT(GSM48_RR_ST_DEDICATED),
- RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
-#endif
-
{SBIT(GSM48_RR_ST_DEDICATED),
RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind},
};
@@ -5359,11 +6818,24 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg)
"%s\n", ms->name, rsl_msg_name(msg_type),
gsm48_rr_state_names[rr->state]);
- if (rr->state == GSM48_RR_ST_CONN_PEND
- && msg_type == RSL_MT_CHAN_CONF) {
- rc = gsm48_rr_tx_rand_acc(ms, msg);
- msgb_free(msg);
- return rc;
+ if (msg_type == RSL_MT_CHAN_CONF) {
+ /* Recevie confirm to get the FN when the access burst was transmitted on VGCS channel. */
+ if (rr->state == GSM48_RR_ST_CONN_PEND && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
+ rc = gsm48_rr_uplink_access(ms, msg);
+ msgb_free(msg);
+ return rc;
+ }
+ /* Ignore subsequent access bursts in dedicated group transmit mode. */
+ if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) {
+ msgb_free(msg);
+ return 0;
+ }
+ /* Recevie confirm to get the FN when the access burst was transmitted on CCCH. */
+ if (rr->state == GSM48_RR_ST_CONN_PEND) {
+ rc = gsm48_rr_tx_rand_acc(ms, msg);
+ msgb_free(msg);
+ return rc;
+ }
}
LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");
@@ -5415,6 +6887,20 @@ static struct rrdownstate {
{SBIT(GSM48_RR_ST_CONN_PEND) |
SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */
GSM48_RR_ABORT_REQ, gsm48_rr_abort_req},
+
+ /* NOTE: If not IDLE, it is rejected there. */
+ {ALL_STATES, /* 3.3.3.2 */
+ GSM48_RR_GROUP_REQ, gsm48_rr_group_req},
+
+ {ALL_STATES,
+ GSM48_RR_GROUP_REL_REQ, gsm48_rr_group_rel_req},
+
+ /* NOTE: If not IDLE, it is rejected there. */
+ {ALL_STATES, /* 3.3.1.2 */
+ GSM48_RR_UPLINK_REQ, gsm48_rr_uplink_req},
+
+ {ALL_STATES, /* 3.4.13.4 */
+ GSM48_RR_UPLINK_REL_REQ, gsm48_rr_uplink_rel_req},
};
#define RRDOWNSLLEN \
@@ -5509,8 +6995,11 @@ int gsm48_rr_init(struct osmocom_ms *ms)
start_rr_t_meas(rr, 1, 0);
- rr->audio_mode = AUDIO_TX_MICROPHONE | AUDIO_RX_SPEAKER;
rr->tch_loop_mode = L1CTL_TCH_LOOP_OPEN;
+ rr->audio_mode = 0x00; /* set in tch_{voice,data}_state_init() */
+
+ /* List of notifications about ongoing ASCI calls */
+ INIT_LLIST_HEAD(&rr->vgcs.notif_list);
return 0;
}
@@ -5540,6 +7029,12 @@ int gsm48_rr_exit(struct osmocom_ms *ms)
stop_rr_t3122(rr);
stop_rr_t3124(rr);
stop_rr_t3126(rr);
+ stop_rr_t_ul_free(rr);
+ stop_rr_t3128(rr);
+ stop_rr_t3130(rr);
+
+ /* Free pending list entries. */
+ asci_notif_list_free(rr);
return 0;
}
@@ -5583,112 +7078,29 @@ static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro)
osmo_timer_schedule(&rr->t3124, sec, micro);
}
-/* send HANDOVER ACCESS burst (9.1.14) */
-static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms)
-{
- nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS");
- if (!nmsg)
- return -ENOMEM;
- *msgb_put(nmsg, 1) = rr->hando_ref;
- todo burst
- return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg, 0);
-}
-
-/* send next channel request in dedicated state */
-static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm48_rrlayer *rr = &ms->rrlayer;
- struct msgb *nmsg;
- int s;
-
- if (rr->modify_state != GSM48_RR_MOD_HANDO) {
- LOGP(DRR, LOGL_NOTICE, "Random access confirm, but not in handover state.\n");
- return 0;
- }
-
- /* send up to four handover access bursts */
- if (rr->hando_acc_left) {
- rr->hando_acc_left--;
- gsm48_rr_tx_hando_access(ms);
- return;
- }
-
- /* start timer for sending next HANDOVER ACCESS bursts afterwards */
- if (!osmo_timer_pending(&rr->t3124)) {
- if (allocated channel is SDCCH)
- start_rr_t3124(rr, GSM_T3124_675);
- else
- start_rr_t3124(rr, GSM_T3124_320);
- }
- if (!rr->n_chan_req) {
- start_rr_t3126(rr, 5, 0); /* TODO improve! */
- return 0;
- }
- rr->n_chan_req--;
-
- /* wait for PHYSICAL INFORMATION message or T3124 timeout */
- return 0;
-
-}
-
#endif
-#define LOG_FRAME_VERIFY(mode, level, fmt, args...) \
- LOGP(DRR, level, "Voice frame, mode=%s: " fmt, get_value_string(gsm48_chan_mode_names, mode), ## args)
-
-int voice_frame_verify(enum gsm48_chan_mode mode, uint8_t *frame, size_t frame_len)
-{
- switch (mode) {
- case GSM48_CMODE_SPEECH_V1:
- /* FIXME: this is FR only, check for TCH/F (FR) and TCH/H (HR) */
- /* RFC 3551, section 4.5.8 GSM */
- if (frame_len != GSM_FR_BYTES) {
- LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect length (%zu != %u)\n", frame_len, GSM_FR_BYTES);
- return -2;
- }
- if ((frame[0] >> 4) != 0xd) {
- LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect magic (%u != 0xd)\n", frame[0] >> 4);
- return -3;
- }
- break;
- case GSM48_CMODE_SPEECH_EFR:
- /* RFC 3551, section 4.5.9 GSM-EFR */
- if (frame_len != GSM_EFR_BYTES) {
- LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect length (%zu != %u)\n", frame_len, GSM_EFR_BYTES);
- return -4;
- }
- if ((frame[0] >> 4) != 0xc) {
- LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect magic (%u != 0xc)\n", frame[0] >> 4);
- return -5;
- }
- break;
- default:
- LOG_FRAME_VERIFY(mode, LOGL_ERROR, "not implemented\n");
- return -1;
- }
- return 0;
-}
-
-int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg)
+int gsm48_rr_tx_traffic(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
uint8_t ch_type, ch_subch, ch_ts;
- struct l1ctl_traffic_req *tr;
if (!rr->dm_est) {
LOGP(DRR, LOGL_INFO, "Current channel is not active\n");
goto error;
}
- rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts);
- if (ch_type != RSL_CHAN_Bm_ACCHs) {
- LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH/F\n");
+ if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, rr->cd_now.chan_nr);
goto error;
}
- tr = (struct l1ctl_traffic_req *)msg->l2h;
- if (voice_frame_verify(rr->cd_now.mode, tr->data, msgb_l2len(msg)) < 0)
+ if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) {
+ LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH\n");
goto error;
+ }
return l1ctl_tx_traffic_req(ms, msg, rr->cd_now.chan_nr, 0);
error:
@@ -5708,10 +7120,16 @@ int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode)
if (!rr->dm_est)
return 0;
- rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) {
+ LOGP(DRSL, LOGL_ERROR,
+ "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n",
+ __func__, rr->cd_now.chan_nr);
+ return -EINVAL;
+ }
+
if (ch_type != RSL_CHAN_Bm_ACCHs
&& ch_type != RSL_CHAN_Lm_ACCHs)
return 0;
- return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode, rr->tch_loop_mode);
+ return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode, rr->cd_now.tch_flags, rr->tch_loop_mode);
}
diff --git a/src/host/layer23/src/mobile/main.c b/src/host/layer23/src/mobile/main.c
index b1e0940b..8720ea76 100644
--- a/src/host/layer23/src/mobile/main.c
+++ b/src/host/layer23/src/mobile/main.c
@@ -15,14 +15,12 @@
* 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 <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/vty.h>
#include <osmocom/bb/mobile/app_mobile.h>
#include <osmocom/core/talloc.h>
@@ -31,6 +29,10 @@
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/application.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/ports.h>
#include <arpa/inet.h>
@@ -46,51 +48,43 @@
#include <time.h>
#include <libgen.h>
+#include "config.h"
+
void *l23_ctx = NULL;
+struct l23_global_config l23_cfg;
struct llist_head ms_list;
-static char *gsmtap_ip = 0;
static const char *custom_cfg_file = NULL;
-struct gsmtap_inst *gsmtap_inst = NULL;
+static const char *log_cat_mask = NULL;
+static char *config_file = NULL;
char *config_dir = NULL;
-int use_mncc_sock = 0;
int daemonize = 0;
+int quit = 0;
-int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg);
+int (*l23_app_start)(void) = NULL;
+int (*l23_app_work)(void) = NULL;
+int (*l23_app_exit)(void) = NULL;
int mobile_delete(struct osmocom_ms *ms, int force);
-int mobile_signal_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data);
int mobile_work(struct osmocom_ms *ms);
int mobile_exit(struct osmocom_ms *ms, int force);
const char *debug_default =
- "DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DSAP:DGPS:DMOB:DPRIM:DLUA";
-
-const char *openbsc_copyright =
- "Copyright (C) 2010-2015 Andreas Eversberg, Sylvain Munaut, Holger Freyther, Harald Welte\n"
- "Contributions by Alex Badea, Pablo Neira, Steve Markgraf and others\n\n"
- "License GPLv2+: GNU GPL version 2 or later "
- "<http://gnu.org/licenses/gpl.html>\n"
- "This is free software: you are free to change and redistribute it.\n"
- "There is NO WARRANTY, to the extent permitted by law.\n";
+ "DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DSAP:DGPS:DMOB:DPRIM:DLUA:DGAPK";
static void print_usage(const char *app)
{
printf("Usage: %s\n", app);
}
-static void print_help()
+static void print_help(void)
{
printf(" Some help...\n");
printf(" -h --help this text\n");
- printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n");
printf(" -d --debug Change debug flags. default: %s\n",
debug_default);
printf(" -D --daemonize Run as daemon\n");
printf(" -c --config-file filename The config file to use.\n");
- printf(" -m --mncc-sock Disable built-in MNCC handler and "
- "offer socket\n");
}
static int handle_options(int argc, char **argv)
@@ -99,12 +93,12 @@ static int handle_options(int argc, char **argv)
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
- {"gsmtap-ip", 1, 0, 'i'},
{"debug", 1, 0, 'd'},
{"daemonize", 0, 0, 'D'},
{"config-file", 1, 0, 'c'},
- {"mncc-sock", 0, 0, 'm'},
/* DEPRECATED options, to be removed */
+ {"gsmtap-ip", 1, 0, 'i'},
+ {"mncc-sock", 0, 0, 'm'},
{"vty-ip", 1, 0, 'u'},
{"vty-port", 1, 0, 'v'},
{0, 0, 0, 0},
@@ -121,22 +115,26 @@ static int handle_options(int argc, char **argv)
print_help();
exit(0);
break;
- case 'i':
- gsmtap_ip = optarg;
- break;
case 'c':
custom_cfg_file = optarg;
break;
case 'd':
- log_parse_category_mask(osmo_stderr_target, optarg);
+ log_cat_mask = optarg;
break;
case 'D':
daemonize = 1;
break;
- case 'm':
- use_mncc_sock = 1;
- break;
/* DEPRECATED options, to be removed */
+ case 'i':
+ fprintf(stderr, "Option 'i' is deprecated! "
+ "Please use the configuration file "
+ "in order to set GSMTAP parameters.\n");
+ return -EINVAL;
+ case 'm':
+ fprintf(stderr, "Option 'm' is deprecated! "
+ "Please use the configuration file "
+ "in order to change the MNCC handler.\n");
+ return -EINVAL;
case 'u':
case 'v':
fprintf(stderr, "Both 'u' and 'v' options are "
@@ -205,13 +203,60 @@ void sighandler(int sigset)
}
}
+static void print_copyright(void)
+{
+ printf("%s"
+ "%s\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n\n",
+ l23_app_info.copyright ? l23_app_info.copyright : "",
+ l23_app_info.contribution ? l23_app_info.contribution : "");
+}
+
+static int _vty_init(void)
+{
+ int rc;
+
+ OSMO_ASSERT(l23_app_info.vty_info != NULL);
+ l23_app_info.vty_info->tall_ctx = l23_ctx;
+
+ vty_init(l23_app_info.vty_info);
+ logging_vty_add_cmds();
+
+ if (l23_app_info.vty_init != NULL)
+ l23_app_info.vty_init();
+ if (config_file) {
+ LOGP(DLGLOBAL, LOGL_INFO, "Using configuration from '%s'\n", config_file);
+ l23_vty_reading = true;
+ rc = vty_read_config_file(config_file, NULL);
+ l23_vty_reading = false;
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_FATAL,
+ "Failed to parse the configuration file '%s'\n", config_file);
+ return rc;
+ }
+ }
+ rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB);
+ if (rc < 0) {
+ LOGP(DMOB, LOGL_FATAL, "Cannot init VTY on %s port %u: %s\n",
+ vty_get_bind_addr(), OSMO_VTY_PORT_BB, strerror(errno));
+ return rc;
+ }
+ return rc;
+}
+
int main(int argc, char **argv)
{
- char *config_file;
- int quit = 0;
int rc;
- printf("%s\n", openbsc_copyright);
+ print_copyright();
+
+ rc = handle_options(argc, argv);
+ if (rc) { /* Abort in case of parsing errors */
+ fprintf(stderr, "Error in command line options. Exiting.\n");
+ return 1;
+ }
srand(time(NULL));
@@ -224,19 +269,10 @@ int main(int argc, char **argv)
/* Init default stderr logging */
osmo_init_logging2(l23_ctx, &log_info);
- rc = handle_options(argc, argv);
- if (rc) { /* Abort in case of parsing errors */
- fprintf(stderr, "Error in command line options. Exiting.\n");
- return 1;
- }
-
- if (gsmtap_ip) {
- gsmtap_inst = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
- if (!gsmtap_inst) {
- fprintf(stderr, "Failed during gsmtap_init()\n");
- exit(1);
- }
- gsmtap_source_add_sink(gsmtap_inst);
+ rc = l23_app_init();
+ if (rc < 0) {
+ fprintf(stderr, "Failed during l23_app_init()\n");
+ exit(1);
}
if (custom_cfg_file) {
@@ -257,12 +293,32 @@ int main(int argc, char **argv)
config_dir = talloc_strdup(l23_ctx, config_file);
config_dir = dirname(config_dir);
- if (use_mncc_sock)
- rc = l23_app_init(mncc_recv_socket, config_file);
- else
- rc = l23_app_init(NULL, config_file);
- if (rc)
- exit(rc);
+ if (l23_app_info.opt_supported & L23_OPT_VTY) {
+ if (_vty_init() < 0)
+ exit(1);
+ }
+
+ if (log_cat_mask != NULL)
+ log_parse_category_mask(osmo_stderr_target, log_cat_mask);
+
+ if (l23_cfg.gsmtap.remote_host) {
+ l23_cfg.gsmtap.inst = gsmtap_source_init2(l23_cfg.gsmtap.local_host, 0,
+ l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT, 1);
+ if (!l23_cfg.gsmtap.inst) {
+ fprintf(stderr, "Failed during gsmtap_source_init2(%s -> %s:%u)\n",
+ l23_cfg.gsmtap.local_host, l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT);
+ exit(1);
+ }
+ gsmtap_source_add_sink(l23_cfg.gsmtap.inst);
+ }
+
+ if (l23_app_start) {
+ rc = l23_app_start();
+ if (rc < 0) {
+ fprintf(stderr, "Failed during l23_app_start()\n");
+ exit(1);
+ }
+ }
signal(SIGINT, sighandler);
signal(SIGTSTP, sighandler);
@@ -281,13 +337,18 @@ int main(int argc, char **argv)
}
while (1) {
- l23_app_work(&quit);
+ l23_app_work();
if (quit && llist_empty(&ms_list))
break;
osmo_select_main(0);
}
- l23_app_exit();
+ if (l23_app_exit)
+ rc = l23_app_exit();
+
+ if (l23_app_info.opt_supported & L23_OPT_VTY)
+ telnet_exit();
+
log_fini();
talloc_free(config_file);
diff --git a/src/host/layer23/src/mobile/mncc_sock.c b/src/host/layer23/src/mobile/mncc_sock.c
index 5ea6feba..d31df291 100644
--- a/src/host/layer23/src/mobile/mncc_sock.c
+++ b/src/host/layer23/src/mobile/mncc_sock.c
@@ -14,10 +14,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>
@@ -50,13 +46,19 @@ int mncc_sock_from_cc(struct mncc_sock_state *state, struct msgb *msg)
/* Check if we currently have a MNCC handler connected */
if (state->conn_bfd.fd < 0) {
+ struct gsm_mncc mncc_out;
LOGP(DMNCC, LOGL_ERROR, "mncc_sock receives %s for external CC app "
"but socket is gone\n", get_mncc_name(msg_type));
- if (msg_type != GSM_TCHF_FRAME
- && msg_type != GSM_TCHF_FRAME_EFR
- && msg_type != MNCC_REL_IND) {
+ switch (msg_type) {
+ case MNCC_REL_IND:
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ case GSM_TCHH_FRAME:
+ case GSM_TCH_FRAME_AMR:
+ case GSM_BAD_FRAME:
+ break;
+ default:
/* release the request */
- struct gsm_mncc mncc_out;
memset(&mncc_out, 0, sizeof(mncc_out));
mncc_out.callref = mncc_in->callref;
mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU,
@@ -87,9 +89,9 @@ static void mncc_sock_close(struct mncc_sock_state *state)
LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has closed connection\n");
+ osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
- osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
osmo_fd_read_enable(&state->listen_bfd);
diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c
index 9a37b973..67cf5a72 100644
--- a/src/host/layer23/src/mobile/mnccms.c
+++ b/src/host/layer23/src/mobile/mnccms.c
@@ -13,10 +13,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 <stdint.h>
@@ -26,9 +22,12 @@
#include <stdlib.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/settings.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/mncc_ms.h>
#include <osmocom/bb/mobile/vty.h>
@@ -36,7 +35,15 @@
static uint32_t new_callref = 1;
static LLIST_HEAD(call_list);
-static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc);
+static const char * const gsm_call_type_str[] = {
+ [GSM_CALL_T_UNKNOWN] = "unknown",
+ [GSM_CALL_T_VOICE] = "voice",
+ [GSM_CALL_T_DATA] = "data",
+ [GSM_CALL_T_DATA_FAX] = "fax",
+};
+
+static int dtmf_statemachine(struct gsm_call *call,
+ const struct gsm_mncc *mncc);
static void timeout_dtmf(void *arg);
/*
@@ -82,72 +89,72 @@ struct gsm_call *get_call_ref(uint32_t callref)
return NULL;
}
-static int8_t mncc_get_bearer(struct gsm_settings *set, uint8_t speech_ver)
+static int8_t mncc_get_bearer(const struct gsm_settings *set, uint8_t speech_ver)
{
switch (speech_ver) {
- case 4:
+ case GSM48_BCAP_SV_AMR_F:
if (set->full_v3)
LOGP(DMNCC, LOGL_INFO, " net suggests full rate v3\n");
else {
LOGP(DMNCC, LOGL_INFO, " full rate v3 not supported\n");
- speech_ver = -1;
+ return -1;
}
break;
- case 2:
+ case GSM48_BCAP_SV_EFR:
if (set->full_v2)
LOGP(DMNCC, LOGL_INFO, " net suggests full rate v2\n");
else {
LOGP(DMNCC, LOGL_INFO, " full rate v2 not supported\n");
- speech_ver = -1;
+ return -1;
}
break;
- case 0: /* mandatory */
+ case GSM48_BCAP_SV_FR: /* mandatory */
if (set->full_v1)
LOGP(DMNCC, LOGL_INFO, " net suggests full rate v1\n");
else {
LOGP(DMNCC, LOGL_INFO, " full rate v1 not supported\n");
- speech_ver = -1;
+ return -1;
}
break;
- case 5:
+ case GSM48_BCAP_SV_AMR_H:
if (set->half_v3)
LOGP(DMNCC, LOGL_INFO, " net suggests half rate v3\n");
else {
LOGP(DMNCC, LOGL_INFO, " half rate v3 not supported\n");
- speech_ver = -1;
+ return -1;
}
break;
- case 1:
+ case GSM48_BCAP_SV_HR:
if (set->half_v1)
LOGP(DMNCC, LOGL_INFO, " net suggests half rate v1\n");
else {
LOGP(DMNCC, LOGL_INFO, " half rate v1 not supported\n");
- speech_ver = -1;
+ return -1;
}
break;
default:
LOGP(DMNCC, LOGL_INFO, " net suggests unknown speech version "
"%d\n", speech_ver);
- speech_ver = -1;
+ return -1;
}
return speech_ver;
}
-static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver,
- struct gsm_mncc *mncc)
+static void mncc_set_bcap_speech(struct gsm_mncc *mncc,
+ const struct gsm_settings *set,
+ int speech_ver)
{
- struct gsm_settings *set = &ms->settings;
int i = 0;
mncc->fields |= MNCC_F_BEARER_CAP;
- mncc->bearer_cap.coding = 0;
+ mncc->bearer_cap.coding = GSM48_BCAP_CODING_GSM_STD;
if (set->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH
&& (set->half_v1 || set->half_v3)) {
- mncc->bearer_cap.radio = 3;
+ mncc->bearer_cap.radio = GSM48_BCAP_RRQ_DUAL_FR;
LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n");
} else {
- mncc->bearer_cap.radio = 1;
+ mncc->bearer_cap.radio = GSM48_BCAP_RRQ_FR_ONLY;
LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n");
}
mncc->bearer_cap.speech_ctm = 0;
@@ -155,41 +162,379 @@ static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver,
if (speech_ver < 0) {
/* if half rate is supported and preferred */
if (set->half_v3 && set->half && set->half_prefer) {
- mncc->bearer_cap.speech_ver[i++] = 5;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_AMR_H;
LOGP(DMNCC, LOGL_INFO, " support half rate v3\n");
}
if (set->half_v1 && set->half && set->half_prefer) {
- mncc->bearer_cap.speech_ver[i++] = 1;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_HR;
LOGP(DMNCC, LOGL_INFO, " support half rate v1\n");
}
/* if full rate is supported */
if (set->full_v3) {
- mncc->bearer_cap.speech_ver[i++] = 4;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_AMR_F;
LOGP(DMNCC, LOGL_INFO, " support full rate v3\n");
}
if (set->full_v2) {
- mncc->bearer_cap.speech_ver[i++] = 2;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_EFR;
LOGP(DMNCC, LOGL_INFO, " support full rate v2\n");
}
if (set->full_v1) { /* mandatory, so it's always true */
- mncc->bearer_cap.speech_ver[i++] = 0;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_FR;
LOGP(DMNCC, LOGL_INFO, " support full rate v1\n");
}
/* if half rate is supported and not preferred */
if (set->half_v3 && set->half && !set->half_prefer) {
- mncc->bearer_cap.speech_ver[i++] = 5;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_AMR_H;
LOGP(DMNCC, LOGL_INFO, " support half rate v3\n");
}
if (set->half_v1 && set->half && !set->half_prefer) {
- mncc->bearer_cap.speech_ver[i++] = 1;
+ mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_HR;
LOGP(DMNCC, LOGL_INFO, " support half rate v1\n");
}
/* if specific speech_ver is given (it must be supported) */
} else
mncc->bearer_cap.speech_ver[i++] = speech_ver;
mncc->bearer_cap.speech_ver[i] = -1; /* end of list */
- mncc->bearer_cap.transfer = 0;
- mncc->bearer_cap.mode = 0;
+ mncc->bearer_cap.transfer = GSM48_BCAP_ITCAP_SPEECH;
+ mncc->bearer_cap.mode = GSM48_BCAP_TMOD_CIRCUIT;
+}
+
+static const struct bcap_data_set {
+ enum gsm48_bcap_ra ra;
+ enum gsm48_bcap_interm_rate ir;
+ enum gsm48_bcap_user_rate ur;
+ enum gsm48_bcap_modem_type mt;
+} bcap_data_set_map[] = {
+ [DATA_CALL_TR_V21_300] = {
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_300,
+ .mt = GSM48_BCAP_MT_V21,
+ },
+ [DATA_CALL_TR_V22_1200] = {
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_1200,
+ .mt = GSM48_BCAP_MT_V22,
+ },
+ [DATA_CALL_TR_V23_1200_75] = {
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_1200_75,
+ .mt = GSM48_BCAP_MT_V23,
+ },
+ [DATA_CALL_TR_V22bis_2400] = {
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_2400,
+ .mt = GSM48_BCAP_MT_V22bis,
+ },
+ [DATA_CALL_TR_V26ter_2400] = {
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_2400,
+ .mt = GSM48_BCAP_MT_V26ter,
+ },
+ [DATA_CALL_TR_V32_4800] = {
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_4800,
+ .mt = GSM48_BCAP_MT_V32,
+ },
+ [DATA_CALL_TR_V32_9600] = {
+ .ir = GSM48_BCAP_IR_16k,
+ .ur = GSM48_BCAP_UR_9600,
+ .mt = GSM48_BCAP_MT_V32,
+ },
+#if 0
+ [DATA_CALL_TR_V34_9600] = {
+ .ir = GSM48_BCAP_IR_16k,
+ .ur = GSM48_BCAP_UR_9600,
+ .mt = GSM48_BCAP_MT_V32,
+ /* (Octet 6c) Modem type: According to ITU-T Rec. V.32
+ * (Octet 6d) Other modem type: According to ITU-T Rec. V.34 (2)
+ * TODO: gsm48_encode_bearer_cap() does not support octet 6d */
+ },
+#endif
+ [DATA_CALL_TR_V110_300] = {
+ .ra = GSM48_BCAP_RA_V110_X30,
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_300,
+ },
+ [DATA_CALL_TR_V110_1200] = {
+ .ra = GSM48_BCAP_RA_V110_X30,
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_1200,
+ },
+ [DATA_CALL_TR_V110_2400] = {
+ .ra = GSM48_BCAP_RA_V110_X30,
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_2400,
+ },
+ [DATA_CALL_TR_V110_4800] = {
+ .ra = GSM48_BCAP_RA_V110_X30,
+ .ir = GSM48_BCAP_IR_8k,
+ .ur = GSM48_BCAP_UR_4800,
+ },
+ [DATA_CALL_TR_V110_9600] = {
+ .ra = GSM48_BCAP_RA_V110_X30,
+ .ir = GSM48_BCAP_IR_16k,
+ .ur = GSM48_BCAP_UR_9600,
+ },
+};
+
+static void mncc_set_bcap_data(struct gsm_mncc *mncc,
+ const struct gsm_settings *set,
+ enum gsm_call_type call_type)
+{
+ const struct data_call_params *cp = &set->call_params.data;
+ struct gsm_mncc_bearer_cap *bcap = &mncc->bearer_cap;
+ const struct bcap_data_set *ds;
+
+ OSMO_ASSERT(cp->type_rate < ARRAY_SIZE(bcap_data_set_map));
+ ds = &bcap_data_set_map[cp->type_rate];
+ if (ds->ir == 0 && ds->ur == 0) {
+ LOGP(DMNCC, LOGL_ERROR, "AT+CBST=%d is not supported\n", cp->type_rate);
+ return;
+ }
+
+ mncc->fields |= MNCC_F_BEARER_CAP;
+
+ *bcap = (struct gsm_mncc_bearer_cap) {
+ /* .transfer is set below */
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ /* .radio is set below */
+ .data = {
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .rate_adaption = ds->ra,
+ .interm_rate = ds->ir,
+ .user_rate = ds->ur,
+ .modem_type = ds->mt,
+ .transp = cp->transp,
+
+ /* async call params */
+ .async = cp->is_async,
+ .nr_data_bits = cp->nr_data_bits,
+ .nr_stop_bits = cp->nr_stop_bits,
+ .parity = cp->parity,
+ },
+ };
+
+ /* Radio channel requirement (octet 3) */
+ if (set->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH) {
+ if (set->half_prefer)
+ bcap->radio = GSM48_BCAP_RRQ_DUAL_HR;
+ else
+ bcap->radio = GSM48_BCAP_RRQ_DUAL_FR;
+ LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n");
+ } else {
+ bcap->radio = GSM48_BCAP_RRQ_FR_ONLY;
+ LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n");
+ }
+
+ /* Information transfer capability (octet 3) */
+ switch (call_type) {
+ case GSM_CALL_T_DATA:
+ if (ds->mt == GSM48_BCAP_MT_NONE)
+ bcap->transfer = GSM_MNCC_BCAP_UNR_DIG;
+ else /* analog modem */
+ bcap->transfer = GSM_MNCC_BCAP_AUDIO;
+ break;
+ case GSM_CALL_T_DATA_FAX:
+ bcap->transfer = GSM_MNCC_BCAP_FAX_G3;
+ break;
+ case GSM_CALL_T_VOICE:
+ default: /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ /* FAX calls are special (see 3GPP TS 24.008, Annex D.3) */
+ if (call_type == GSM_CALL_T_DATA_FAX) {
+ bcap->data.rate_adaption = GSM48_BCAP_RA_NONE;
+ bcap->data.async = 0; /* shall be sync */
+ bcap->data.transp = GSM48_BCAP_TR_TRANSP;
+ bcap->data.modem_type = GSM48_BCAP_MT_NONE;
+ }
+}
+
+/* Check the given Bearer Capability, select first supported speech codec version.
+ * The choice between half-rate and full-rate is made based on current settings.
+ * Return a selected codec or -1 if no speech codec was selected. */
+static int mncc_handle_bcap_speech(const struct gsm_mncc_bearer_cap *bcap,
+ const struct gsm_settings *set)
+{
+ int speech_ver_half = -1;
+ int speech_ver = -1;
+
+ for (unsigned int i = 0; bcap->speech_ver[i] >= 0; i++) {
+ int temp = mncc_get_bearer(set, bcap->speech_ver[i]);
+ switch (temp) {
+ case GSM48_BCAP_SV_AMR_H:
+ case GSM48_BCAP_SV_HR:
+ /* only the first half rate */
+ if (speech_ver_half < 0)
+ speech_ver_half = temp;
+ break;
+ default:
+ if (temp < 0)
+ continue;
+ /* only the first full rate */
+ if (speech_ver < 0)
+ speech_ver = temp;
+ break;
+ }
+ }
+
+ /* half and full given */
+ if (speech_ver_half >= 0 && speech_ver >= 0) {
+ if (set->half_prefer) {
+ LOGP(DMNCC, LOGL_INFO, " both supported"
+ " codec rates are given, using "
+ "preferred half rate\n");
+ speech_ver = speech_ver_half;
+ } else {
+ LOGP(DMNCC, LOGL_INFO, " both supported"
+ " codec rates are given, using "
+ "preferred full rate\n");
+ }
+ } else if (speech_ver_half < 0 && speech_ver < 0) {
+ LOGP(DMNCC, LOGL_INFO, " no supported codec "
+ "rate is given\n");
+ /* only half rate is given, use it */
+ } else if (speech_ver_half >= 0) {
+ LOGP(DMNCC, LOGL_INFO, " only supported half "
+ "rate codec is given, using it\n");
+ speech_ver = speech_ver_half;
+ /* only full rate is given, use it */
+ } else {
+ LOGP(DMNCC, LOGL_INFO, " only supported full "
+ "rate codec is given, using it\n");
+ }
+
+ return speech_ver;
+}
+
+/* Check the given Bearer Capability for a data call (CSD).
+ * Return 0 if the bearer is accepted, otherwise return -1. */
+static int mncc_handle_bcap_data(const struct gsm_mncc_bearer_cap *bcap,
+ const struct gsm_settings *set)
+{
+ switch (bcap->transfer) {
+ case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ if (bcap->data.rate_adaption != GSM48_BCAP_RA_V110_X30) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): Rate adaption (octet 5) 0x%02x is not supported\n",
+ __func__, bcap->data.rate_adaption);
+ return -ENOTSUP;
+ }
+ break;
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
+ case GSM48_BCAP_ITCAP_FAX_G3:
+ if (bcap->data.rate_adaption != GSM48_BCAP_RA_NONE) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): Rate adaption (octet 5) 0x%02x was expected to be NONE\n",
+ __func__, bcap->data.rate_adaption);
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (bcap->data.sig_access != GSM48_BCAP_SA_I440_I450) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): Signalling access protocol (octet 5) 0x%02x is not supported\n",
+ __func__, bcap->data.sig_access);
+ return -ENOTSUP;
+ }
+
+#define BCAP_RATE(interm_rate, user_rate) \
+ ((interm_rate << 8) | (user_rate << 0))
+
+ switch (BCAP_RATE(bcap->data.interm_rate, bcap->data.user_rate)) {
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_300):
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_1200):
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_2400):
+ if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): wrong user-rate 0x%02x for a non-transparent call\n",
+ __func__, bcap->data.user_rate);
+ return -EINVAL;
+ }
+ /* fall-through */
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_4800):
+ case BCAP_RATE(GSM48_BCAP_IR_16k, GSM48_BCAP_UR_9600):
+ if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): only transparent calls are supported so far\n",
+ __func__);
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): IR 0x%02x / UR 0x%02x combination is not supported\n",
+ __func__, bcap->data.interm_rate, bcap->data.user_rate);
+ return -ENOTSUP;
+ }
+
+#undef BCAP_RATE
+
+ return 0;
+}
+
+static int mncc_handle_bcap(struct gsm_mncc *mncc_out, /* CC Call Confirmed */
+ const struct gsm_mncc *mncc_in, /* CC Setup */
+ const struct gsm_settings *set)
+{
+ const struct gsm_mncc_bearer_cap *bcap = &mncc_in->bearer_cap;
+
+ /* 3GPP TS 24.008, section 9.3.2.2 defines several cases in which the
+ * Bearer Capability 1 IE is to be included, provided that at least
+ * one of these conditions is met. */
+
+ /* if the Bearer Capability 1 IE is not present */
+ if (~mncc_in->fields & MNCC_F_BEARER_CAP) {
+ /* ... include our own Bearer Capability, assuming a speech call */
+ mncc_set_bcap_speech(mncc_out, set, -1);
+ return 0;
+ }
+
+ if (bcap->mode != GSM48_BCAP_TMOD_CIRCUIT) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): Transfer mode 0x%02x is not supported\n",
+ __func__, bcap->mode);
+ return -ENOTSUP;
+ }
+ if (bcap->coding != GSM48_BCAP_CODING_GSM_STD) {
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): Coding standard 0x%02x is not supported\n",
+ __func__, bcap->coding);
+ return -ENOTSUP;
+ }
+
+ switch (bcap->transfer) {
+ case GSM48_BCAP_ITCAP_SPEECH:
+ {
+ int speech_ver = mncc_handle_bcap_speech(bcap, set);
+ /* include bearer cap, if not given in setup (see above)
+ * or if multiple codecs are given
+ * or if not only full rate
+ * or if given codec is unimplemented
+ */
+ if (speech_ver < 0)
+ mncc_set_bcap_speech(mncc_out, set, -1);
+ else if (bcap->speech_ver[1] >= 0 || speech_ver != 0)
+ mncc_set_bcap_speech(mncc_out, set, speech_ver);
+ break;
+ }
+ case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
+ case GSM48_BCAP_ITCAP_FAX_G3:
+ return mncc_handle_bcap_data(bcap, set);
+ default:
+ LOGP(DMNCC, LOGL_ERROR,
+ "%s(): Information transfer capability 0x%02x is not supported\n",
+ __func__, bcap->transfer);
+ return -ENOTSUP;
+ }
+
+ return 0;
}
/*
@@ -220,7 +565,7 @@ int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg)
/*
* MNCCms call application via socket
*/
-int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg)
+int mncc_recv_external(struct osmocom_ms *ms, int msg_type, void *arg)
{
struct mncc_sock_state *state = ms->mncc_entity.sock_state;
struct gsm_mncc *mncc = arg;
@@ -257,14 +602,13 @@ int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg)
* MNCCms basic call application
*/
-int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
+int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg)
{
- struct gsm_settings *set = &ms->settings;
- struct gsm_mncc *data = arg;
+ const struct gsm_settings *set = &ms->settings;
+ const struct gsm_mncc *data = arg;
struct gsm_call *call = get_call_ref(data->callref);
struct gsm_mncc mncc;
uint8_t cause;
- int8_t speech_ver = -1, speech_ver_half = -1, temp;
int first_call = 0;
/* call does not exist */
@@ -290,6 +634,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
return -ENOMEM;
call->ms = ms;
call->callref = data->callref;
+ call->type = GSM_CALL_T_UNKNOWN;
llist_add_tail(&call->entry, &call_list);
}
@@ -298,62 +643,62 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
switch (msg_type) {
case MNCC_DISC_IND:
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
switch (data->cause.value) {
case GSM48_CC_CAUSE_UNASSIGNED_NR:
- vty_notify(ms, "Call: Number not assigned\n");
+ l23_vty_ms_notify(ms, "Call: Number not assigned\n");
break;
case GSM48_CC_CAUSE_NO_ROUTE:
- vty_notify(ms, "Call: Destination unreachable\n");
+ l23_vty_ms_notify(ms, "Call: Destination unreachable\n");
break;
case GSM48_CC_CAUSE_NORM_CALL_CLEAR:
- vty_notify(ms, "Call: Remote hangs up\n");
+ l23_vty_ms_notify(ms, "Call: Remote hangs up\n");
break;
case GSM48_CC_CAUSE_USER_BUSY:
- vty_notify(ms, "Call: Remote busy\n");
+ l23_vty_ms_notify(ms, "Call: Remote busy\n");
break;
case GSM48_CC_CAUSE_USER_NOTRESPOND:
- vty_notify(ms, "Call: Remote not responding\n");
+ l23_vty_ms_notify(ms, "Call: Remote not responding\n");
break;
case GSM48_CC_CAUSE_USER_ALERTING_NA:
- vty_notify(ms, "Call: Remote not answering\n");
+ l23_vty_ms_notify(ms, "Call: Remote not answering\n");
break;
case GSM48_CC_CAUSE_CALL_REJECTED:
- vty_notify(ms, "Call has been rejected\n");
+ l23_vty_ms_notify(ms, "Call has been rejected\n");
break;
case GSM48_CC_CAUSE_NUMBER_CHANGED:
- vty_notify(ms, "Call: Number changed\n");
+ l23_vty_ms_notify(ms, "Call: Number changed\n");
break;
case GSM48_CC_CAUSE_PRE_EMPTION:
- vty_notify(ms, "Call: Cleared due to pre-emption\n");
+ l23_vty_ms_notify(ms, "Call: Cleared due to pre-emption\n");
break;
case GSM48_CC_CAUSE_DEST_OOO:
- vty_notify(ms, "Call: Remote out of order\n");
+ l23_vty_ms_notify(ms, "Call: Remote out of order\n");
break;
case GSM48_CC_CAUSE_INV_NR_FORMAT:
- vty_notify(ms, "Call: Number invalid or incomplete\n");
+ l23_vty_ms_notify(ms, "Call: Number invalid or incomplete\n");
break;
case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN:
- vty_notify(ms, "Call: No channel available\n");
+ l23_vty_ms_notify(ms, "Call: No channel available\n");
break;
case GSM48_CC_CAUSE_NETWORK_OOO:
- vty_notify(ms, "Call: Network out of order\n");
+ l23_vty_ms_notify(ms, "Call: Network out of order\n");
break;
case GSM48_CC_CAUSE_TEMP_FAILURE:
- vty_notify(ms, "Call: Temporary failure\n");
+ l23_vty_ms_notify(ms, "Call: Temporary failure\n");
break;
case GSM48_CC_CAUSE_SWITCH_CONG:
- vty_notify(ms, "Congestion\n");
+ l23_vty_ms_notify(ms, "Congestion\n");
break;
default:
- vty_notify(ms, "Call has been disconnected "
+ l23_vty_ms_notify(ms, "Call has been disconnected "
"(clear cause %d)\n", data->cause.value);
}
LOGP(DMNCC, LOGL_INFO, "Call has been disconnected "
"(cause %d)\n", data->cause.value);
if ((data->fields & MNCC_F_PROGRESS)
&& data->progress.descr == 8) {
- vty_notify(ms, "Please hang up!\n");
+ l23_vty_ms_notify(ms, "Please hang up!\n");
break;
}
free_call(call);
@@ -361,18 +706,18 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
goto release;
case MNCC_REL_IND:
case MNCC_REL_CNF:
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED)
- vty_notify(ms, "Call has been rejected\n");
+ l23_vty_ms_notify(ms, "Call has been rejected\n");
else
- vty_notify(ms, "Call has been released\n");
+ l23_vty_ms_notify(ms, "Call has been released\n");
LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n",
data->cause.value);
free_call(call);
break;
case MNCC_CALL_PROC_IND:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call is proceeding\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call is proceeding\n");
LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n");
if ((data->fields & MNCC_F_BEARER_CAP)
&& data->bearer_cap.speech_ver[0] >= 0) {
@@ -380,94 +725,59 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
}
break;
case MNCC_ALERT_IND:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call is alerting\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call is alerting\n");
LOGP(DMNCC, LOGL_INFO, "Call is alerting\n");
break;
case MNCC_SETUP_CNF:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call is answered\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call is answered\n");
LOGP(DMNCC, LOGL_INFO, "Call is answered\n");
break;
case MNCC_SETUP_IND:
- vty_notify(ms, NULL);
+ l23_vty_ms_notify(ms, NULL);
if (!first_call && !ms->settings.cw) {
- vty_notify(ms, "Incoming call rejected while busy\n");
+ l23_vty_ms_notify(ms, "Incoming call rejected while busy\n");
LOGP(DMNCC, LOGL_INFO, "Incoming call but busy\n");
cause = GSM48_CC_CAUSE_USER_BUSY;
goto release;
}
- /* select first supported speech_ver */
- if ((data->fields & MNCC_F_BEARER_CAP)) {
- int i;
-
- for (i = 0; data->bearer_cap.speech_ver[i] >= 0; i++) {
-
- temp = mncc_get_bearer(set,
- data->bearer_cap.speech_ver[i]);
- if (temp < 0)
- continue;
- if (temp == 5 || temp == 1) { /* half */
- /* only the first half rate */
- if (speech_ver_half < 0)
- speech_ver_half = temp;
- } else {
- /* only the first full rate */
- if (speech_ver < 0)
- speech_ver = temp;
- }
- }
- /* half and full given */
- if (speech_ver_half >= 0 && speech_ver >= 0) {
- if (set->half_prefer) {
- LOGP(DMNCC, LOGL_INFO, " both supported"
- " codec rates are given, using "
- "preferred half rate\n");
- speech_ver = speech_ver_half;
- } else
- LOGP(DMNCC, LOGL_INFO, " both supported"
- " codec rates are given, using "
- "preferred full rate\n");
- } else if (speech_ver_half < 0 && speech_ver < 0) {
- LOGP(DMNCC, LOGL_INFO, " no supported codec "
- "rate is given\n");
- /* only half rate is given, use it */
- } else if (speech_ver_half >= 0) {
- LOGP(DMNCC, LOGL_INFO, " only supported half "
- "rate codec is given, using it\n");
- speech_ver = speech_ver_half;
- /* only full rate is given, use it */
- } else {
- LOGP(DMNCC, LOGL_INFO, " only supported full "
- "rate codec is given, using it\n");
- }
- }
/* presentation allowed if present == 0 */
if (data->calling.present || !data->calling.number[0])
- vty_notify(ms, "Incoming call (anonymous)\n");
+ l23_vty_ms_notify(ms, "Incoming call (anonymous)\n");
else if (data->calling.type == 1)
- vty_notify(ms, "Incoming call (from +%s)\n",
+ l23_vty_ms_notify(ms, "Incoming call (from +%s)\n",
data->calling.number);
else if (data->calling.type == 2)
- vty_notify(ms, "Incoming call (from 0-%s)\n",
+ l23_vty_ms_notify(ms, "Incoming call (from 0-%s)\n",
data->calling.number);
else
- vty_notify(ms, "Incoming call (from %s)\n",
+ l23_vty_ms_notify(ms, "Incoming call (from %s)\n",
data->calling.number);
LOGP(DMNCC, LOGL_INFO, "Incoming call (from %s callref %x)\n",
data->calling.number, call->callref);
memset(&mncc, 0, sizeof(struct gsm_mncc));
mncc.callref = call->callref;
- /* only include bearer cap, if not given in setup
- * or if multiple codecs are given
- * or if not only full rate
- * or if given codec is unimplemented
- */
- if (!(data->fields & MNCC_F_BEARER_CAP) || speech_ver < 0)
- mncc_set_bearer(ms, -1, &mncc);
- else if (data->bearer_cap.speech_ver[1] >= 0
- || speech_ver != 0)
- mncc_set_bearer(ms, speech_ver, &mncc);
+ /* Bearer capability (optional) */
+ if (mncc_handle_bcap(&mncc, data, &ms->settings) != 0) {
+ cause = GSM48_CC_CAUSE_INCOMPAT_DEST;
+ goto release;
+ }
+ switch (mncc.bearer_cap.transfer) {
+ case GSM48_BCAP_ITCAP_SPEECH:
+ call->type = GSM_CALL_T_VOICE;
+ break;
+ case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
+ call->type = GSM_CALL_T_DATA;
+ break;
+ case GSM48_BCAP_ITCAP_FAX_G3:
+ call->type = GSM_CALL_T_DATA_FAX;
+ break;
+ default:
+ call->type = GSM_CALL_T_UNKNOWN;
+ break;
+ }
/* CC capabilities (optional) */
if (ms->settings.cc_dtmf) {
mncc.fields |= MNCC_F_CCCAP;
@@ -490,30 +800,30 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
}
break;
case MNCC_SETUP_COMPL_IND:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call is connected\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call is connected\n");
LOGP(DMNCC, LOGL_INFO, "Call is connected\n");
break;
case MNCC_HOLD_CNF:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call is on hold\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call is on hold\n");
LOGP(DMNCC, LOGL_INFO, "Call is on hold\n");
call->hold = true;
break;
case MNCC_HOLD_REJ:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call hold was rejected\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call hold was rejected\n");
LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n");
break;
case MNCC_RETRIEVE_CNF:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call is retrieved\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call is retrieved\n");
LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n");
call->hold = false;
break;
case MNCC_RETRIEVE_REJ:
- vty_notify(ms, NULL);
- vty_notify(ms, "Call retrieve was rejected\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Call retrieve was rejected\n");
LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n");
break;
case MNCC_FACILITY_IND:
@@ -534,15 +844,16 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
return 0;
}
-int mncc_call(struct osmocom_ms *ms, char *number)
+int mncc_call(struct osmocom_ms *ms, const char *number,
+ enum gsm_call_type call_type)
{
struct gsm_call *call;
struct gsm_mncc setup;
llist_for_each_entry(call, &call_list, entry) {
if (!call->hold) {
- vty_notify(ms, NULL);
- vty_notify(ms, "Please put active call on hold "
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Please put active call on hold "
"first!\n");
LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n");
return -EBUSY;
@@ -554,6 +865,7 @@ int mncc_call(struct osmocom_ms *ms, char *number)
return -ENOMEM;
call->ms = ms;
call->callref = new_callref++;
+ call->type = call_type;
call->init = true;
llist_add_tail(&call->entry, &call_list);
@@ -565,7 +877,8 @@ int mncc_call(struct osmocom_ms *ms, char *number)
/* emergency */
setup.emergency = 1;
} else {
- LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number);
+ LOGP(DMNCC, LOGL_INFO, "Make %s call to %s\n",
+ gsm_call_type_str[call_type], number);
/* called number */
setup.fields |= MNCC_F_CALLED;
if (number[0] == '+') {
@@ -578,7 +891,10 @@ int mncc_call(struct osmocom_ms *ms, char *number)
OSMO_STRLCPY_ARRAY(setup.called.number, number);
/* bearer capability (mandatory) */
- mncc_set_bearer(ms, -1, &setup);
+ if (call_type == GSM_CALL_T_VOICE)
+ mncc_set_bcap_speech(&setup, &ms->settings, -1);
+ else
+ mncc_set_bcap_data(&setup, &ms->settings, call_type);
/* CLIR */
if (ms->settings.clir)
@@ -609,8 +925,8 @@ int mncc_hangup(struct osmocom_ms *ms)
}
if (!found) {
LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "No active call\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No active call\n");
return -EINVAL;
}
@@ -636,14 +952,14 @@ int mncc_answer(struct osmocom_ms *ms)
}
if (!alerting) {
LOGP(DMNCC, LOGL_INFO, "No call alerting\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "No alerting call\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No alerting call\n");
return -EBUSY;
}
if (active) {
LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "Please put active call on hold first!\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Please put active call on hold first!\n");
return -EBUSY;
}
alerting->ring = false;
@@ -667,8 +983,8 @@ int mncc_hold(struct osmocom_ms *ms)
}
if (!found) {
LOGP(DMNCC, LOGL_INFO, "No active call to hold\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "No active call\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No active call\n");
return -EINVAL;
}
@@ -691,26 +1007,26 @@ int mncc_retrieve(struct osmocom_ms *ms, int number)
}
if (active) {
LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "Hold active call first!\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Hold active call first!\n");
return -EINVAL;
}
if (holdnum == 0) {
- vty_notify(ms, NULL);
- vty_notify(ms, "No call on hold!\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No call on hold!\n");
return -EINVAL;
}
if (holdnum > 1 && number <= 0) {
- vty_notify(ms, NULL);
- vty_notify(ms, "Select call 1..%d\n", holdnum);
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Select call 1..%d\n", holdnum);
return -EINVAL;
}
if (holdnum == 1 && number <= 0)
number = 1;
if (number > holdnum) {
- vty_notify(ms, NULL);
- vty_notify(ms, "Given number %d out of range!\n", number);
- vty_notify(ms, "Select call 1..%d\n", holdnum);
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "Given number %d out of range!\n", number);
+ l23_vty_ms_notify(ms, "Select call 1..%d\n", holdnum);
return -EINVAL;
}
@@ -729,7 +1045,8 @@ int mncc_retrieve(struct osmocom_ms *ms, int number)
* DTMF
*/
-static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc)
+static int dtmf_statemachine(struct gsm_call *call,
+ const struct gsm_mncc *mncc)
{
struct osmocom_ms *ms = call->ms;
struct gsm_mncc dtmf;
@@ -795,8 +1112,8 @@ int mncc_dtmf(struct osmocom_ms *ms, char *dtmf)
}
if (!found) {
LOGP(DMNCC, LOGL_INFO, "No active call to send DTMF\n");
- vty_notify(ms, NULL);
- vty_notify(ms, "No active call\n");
+ l23_vty_ms_notify(ms, NULL);
+ l23_vty_ms_notify(ms, "No active call\n");
return -EINVAL;
}
diff --git a/src/host/layer23/src/mobile/primitives.c b/src/host/layer23/src/mobile/primitives.c
index f562466a..4a40b4f7 100644
--- a/src/host/layer23/src/mobile/primitives.c
+++ b/src/host/layer23/src/mobile/primitives.c
@@ -12,10 +12,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 <inttypes.h>
diff --git a/src/host/layer23/src/mobile/script_lua.c b/src/host/layer23/src/mobile/script_lua.c
index 50315bdb..eb3f0085 100644
--- a/src/host/layer23/src/mobile/script_lua.c
+++ b/src/host/layer23/src/mobile/script_lua.c
@@ -12,10 +12,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 <lua.h>
@@ -23,6 +19,7 @@
#include <lauxlib.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/mobile/app_mobile.h>
#include <osmocom/bb/common/logging.h>
diff --git a/src/host/layer23/src/mobile/script_nolua.c b/src/host/layer23/src/mobile/script_nolua.c
index 61466f77..5973a44c 100644
--- a/src/host/layer23/src/mobile/script_nolua.c
+++ b/src/host/layer23/src/mobile/script_nolua.c
@@ -12,10 +12,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 <osmocom/bb/common/osmocom_data.h>
diff --git a/src/host/layer23/src/mobile/tch.c b/src/host/layer23/src/mobile/tch.c
new file mode 100644
index 00000000..b78682f5
--- /dev/null
+++ b/src/host/layer23/src/mobile/tch.c
@@ -0,0 +1,234 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2022-2024 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 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.
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/mobile/mncc.h>
+#include <osmocom/bb/mobile/mncc_sock.h>
+#include <osmocom/bb/mobile/transaction.h>
+#include <osmocom/bb/mobile/tch.h>
+
+int tch_voice_state_init(struct gsm_trans *trans,
+ struct tch_voice_state *state);
+int tch_data_state_init(struct gsm_trans *trans,
+ struct tch_data_state *state);
+
+void tch_voice_state_free(struct tch_voice_state *state);
+void tch_data_state_free(struct tch_data_state *state);
+
+int tch_voice_recv(struct osmocom_ms *ms, struct msgb *msg);
+int tch_data_recv(struct osmocom_ms *ms, struct msgb *msg);
+
+/* Receive a Downlink traffic (voice/data) frame from the lower layers */
+static int tch_recv_cb(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct tch_state *state = ms->tch_state;
+
+ if (state == NULL) {
+ msgb_free(msg);
+ return 0;
+ }
+
+ if (state->is_voice) {
+ return tch_voice_recv(ms, msg);
+ } else {
+ /* Rx only mode makes no sense for data calls, so we discard
+ * received DL frames and thus do not trigger sending UL frames. */
+ if (!state->rx_only)
+ return tch_data_recv(ms, msg);
+ msgb_free(msg);
+ return 0;
+ }
+}
+
+/* Send an Uplink voice frame to the lower layers */
+int tch_send_msg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct tch_state *state = ms->tch_state;
+
+ if (state == NULL || state->rx_only) {
+ /* TODO: fix callers and print a warning here */
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ return gsm48_rr_tx_traffic(ms, msg);
+}
+
+/* tch_send_msg() wrapper accepting an MNCC structure */
+int tch_send_mncc_frame(struct osmocom_ms *ms, const struct gsm_data_frame *frame)
+{
+ struct msgb *nmsg;
+ int len;
+
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ len = GSM_FR_BYTES;
+ break;
+ case GSM_TCHF_FRAME_EFR:
+ len = GSM_EFR_BYTES;
+ break;
+ case GSM_TCHH_FRAME:
+ len = GSM_HR_BYTES;
+ break;
+ /* TODO: case GSM_TCH_FRAME_AMR (variable length) */
+ /* TODO: case GSM_BAD_FRAME (empty?) */
+ default:
+ LOGP(DL1C, LOGL_ERROR, "%s(): msg_type=0x%02x: not implemented\n",
+ __func__, frame->msg_type);
+ return -EINVAL;
+ }
+
+ nmsg = msgb_alloc_headroom(len + 64, 64, "TCH/F");
+ if (!nmsg)
+ return -ENOMEM;
+ nmsg->l2h = msgb_put(nmsg, len);
+ memcpy(nmsg->l2h, frame->data, len);
+
+ return tch_send_msg(ms, nmsg);
+}
+
+static void tch_trans_cstate_active_cb(struct gsm_trans *trans, bool rx_only)
+{
+ struct osmocom_ms *ms = trans->ms;
+ const struct gsm48_rrlayer *rr = &ms->rrlayer;
+ const struct gsm48_rr_cd *cd = &rr->cd_now;
+ struct tch_state *state;
+
+ if (ms->tch_state != NULL) {
+ ms->tch_state->rx_only = rx_only;
+ return; /* TODO: handle modify? */
+ }
+
+ state = talloc_zero(ms, struct tch_state);
+ OSMO_ASSERT(state != NULL);
+ ms->tch_state = state;
+ ms->tch_state->rx_only = rx_only;
+
+ switch (cd->mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ state->is_voice = true;
+ state->voice.handler = ms->settings.tch_voice.io_handler;
+ if (tch_voice_state_init(trans, &state->voice) != 0)
+ goto exit_free;
+ break;
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ state->is_voice = false;
+ state->data.handler = ms->settings.tch_data.io_handler;
+ if (tch_data_state_init(trans, &state->data) != 0)
+ goto exit_free;
+ break;
+ case GSM48_CMODE_SIGN:
+ default:
+ LOGP(DL1C, LOGL_ERROR, "Unhandled channel mode %s\n",
+ get_value_string(gsm48_chan_mode_names, cd->mode));
+exit_free:
+ talloc_free(state);
+ ms->tch_state = NULL;
+ return;
+ }
+
+ /* rr->audio_mode has been set by tch_{voice,data}_state_init(), apply it */
+ gsm48_rr_audio_mode(ms, rr->audio_mode);
+}
+
+static void tch_trans_free_cb(struct gsm_trans *trans)
+{
+ struct osmocom_ms *ms = trans->ms;
+ struct tch_state *state = ms->tch_state;
+
+ if (state == NULL)
+ return;
+ if (state->is_voice)
+ tch_voice_state_free(&state->voice);
+ else
+ tch_data_state_free(&state->data);
+ ms->rrlayer.audio_mode = 0x00;
+
+ talloc_free(state);
+ ms->tch_state = NULL;
+}
+
+static void tch_trans_state_chg_cb(struct gsm_trans *trans)
+{
+ switch (trans->cc.state) {
+ case GSM_CSTATE_CALL_DELIVERED: /* MO call: Rx CC ALERTING */
+ tch_trans_cstate_active_cb(trans, true);
+ break;
+ case GSM_CSTATE_ACTIVE: /* MO/MT call: Rx CC CONNECT */
+ tch_trans_cstate_active_cb(trans, false);
+ break;
+ case GSM_CSTATE_NULL:
+ tch_trans_free_cb(trans);
+ break;
+ }
+}
+
+/* a call-back for CC (Call Control) transaction related events */
+static int tch_trans_signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_trans *trans = signal_data;
+
+ OSMO_ASSERT(subsys == SS_L23_TRANS);
+ OSMO_ASSERT(trans != NULL);
+
+ /* we only care about CC transactions here */
+ if (trans->protocol != GSM48_PDISC_CC)
+ return 0;
+
+ switch ((enum osmobb_l23_trans_sig)signal) {
+ case S_L23_CC_TRANS_ALLOC:
+ break;
+ case S_L23_CC_TRANS_FREE:
+ tch_trans_free_cb(trans);
+ break;
+ case S_L23_CC_TRANS_STATE_CHG:
+ tch_trans_state_chg_cb(trans);
+ break;
+ }
+
+ return 0;
+}
+
+/* Initialize the TCH router */
+int tch_init(struct osmocom_ms *ms)
+{
+ ms->l1_entity.l1_traffic_ind = &tch_recv_cb;
+
+ osmo_signal_register_handler(SS_L23_TRANS, &tch_trans_signal_cb, ms);
+
+ return 0;
+}
diff --git a/src/host/layer23/src/mobile/tch_data.c b/src/host/layer23/src/mobile/tch_data.c
new file mode 100644
index 00000000..b6b3be6a
--- /dev/null
+++ b/src/host/layer23/src/mobile/tch_data.c
@@ -0,0 +1,587 @@
+/*
+ * (C) 2023-2024 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 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.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/soft_uart.h>
+
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gsm/gsm44021.h>
+
+#include <osmocom/isdn/v110.h>
+#include <osmocom/isdn/v110_ta.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/mobile/mncc.h>
+#include <osmocom/bb/mobile/transaction.h>
+#include <osmocom/bb/mobile/tch.h>
+
+#include <l1ctl_proto.h>
+
+struct csd_v110_frame_desc {
+ uint16_t num_blocks;
+ uint16_t num_bits;
+};
+
+struct csd_v110_lchan_desc {
+ struct csd_v110_frame_desc fr;
+ struct csd_v110_frame_desc hr;
+};
+
+/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */
+const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = {
+#if 0
+ [GSM48_CMODE_DATA_14k5] = {
+ /* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */
+ .fr = { .num_blocks = 1, .num_bits = 290 },
+ },
+#endif
+ [GSM48_CMODE_DATA_12k0] = {
+ /* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */
+ .fr = { .num_blocks = 4, .num_bits = 60 },
+ },
+ [GSM48_CMODE_DATA_6k0] = {
+ /* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */
+ .fr = { .num_blocks = 2, .num_bits = 60 },
+ /* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */
+ .hr = { .num_blocks = 4, .num_bits = 60 },
+ },
+ [GSM48_CMODE_DATA_3k6] = {
+ /* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */
+ .fr = { .num_blocks = 2, .num_bits = 36 },
+ /* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */
+ .hr = { .num_blocks = 4, .num_bits = 36 },
+ },
+};
+
+struct tch_csd_sock_state *tch_csd_sock_init(struct osmocom_ms *ms);
+void tch_csd_sock_recv(struct tch_csd_sock_state *state, struct msgb *msg);
+void tch_csd_sock_send(struct tch_csd_sock_state *state, struct msgb *msg);
+void tch_csd_sock_exit(struct tch_csd_sock_state *state);
+
+static void tch_soft_uart_rx_cb(void *priv, struct msgb *msg, unsigned int flags)
+{
+ struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ LOGP(DCSD, LOGL_DEBUG, "%s(): [flags=0x%08x] %s\n",
+ __func__, flags, msgb_hexdump(msg));
+
+ if (state->sock != NULL && msgb_length(msg) > 0)
+ tch_csd_sock_send(state->sock, msg);
+ else
+ msgb_free(msg);
+}
+
+static void tch_soft_uart_tx_cb(void *priv, struct msgb *msg)
+{
+ struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ tch_csd_sock_recv(state->sock, msg);
+
+ LOGP(DCSD, LOGL_DEBUG, "%s(): [n_bytes=%u/%u] %s\n",
+ __func__, msg->len, msg->data_len, msgb_hexdump(msg));
+}
+
+struct osmo_soft_uart *tch_soft_uart_alloc(struct osmocom_ms *ms,
+ const struct gsm_mncc_bearer_cap *bcap)
+{
+ struct osmo_soft_uart *suart;
+
+ struct osmo_soft_uart_cfg cfg = {
+ .num_data_bits = bcap->data.nr_data_bits,
+ .num_stop_bits = bcap->data.nr_stop_bits,
+ /* .parity_mode is set below */
+ .rx_buf_size = 1024, /* TODO: align with the current TCH mode */
+ .rx_timeout_ms = 100, /* TODO: align with TCH framing interval */
+ .priv = (void *)&ms->tch_state->data,
+ .rx_cb = &tch_soft_uart_rx_cb,
+ .tx_cb = &tch_soft_uart_tx_cb,
+ };
+
+ switch (bcap->data.parity) {
+ case GSM48_BCAP_PAR_ODD:
+ cfg.parity_mode = OSMO_SUART_PARITY_ODD;
+ break;
+ case GSM48_BCAP_PAR_EVEN:
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ break;
+ case GSM48_BCAP_PAR_ZERO:
+ cfg.parity_mode = OSMO_SUART_PARITY_SPACE;
+ break;
+ case GSM48_BCAP_PAR_ONE:
+ cfg.parity_mode = OSMO_SUART_PARITY_MARK;
+ break;
+ case GSM48_BCAP_PAR_NONE:
+ default:
+ cfg.parity_mode = OSMO_SUART_PARITY_NONE;
+ break;
+ }
+
+ suart = osmo_soft_uart_alloc(ms, "csd_soft_uart", &cfg);
+ if (suart == NULL)
+ return NULL;
+
+ osmo_soft_uart_set_rx(suart, true);
+ osmo_soft_uart_set_tx(suart, true);
+
+ return suart;
+}
+
+/*************************************************************************************/
+
+static void tch_v110_ta_rx_cb(void *priv, const ubit_t *buf, size_t buf_size)
+{
+ const struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ if (state->sock != NULL && buf_size > 0) {
+ struct msgb *msg = msgb_alloc(buf_size, __func__);
+ tch_csd_sock_send(state->sock, msg);
+ }
+}
+
+static void tch_v110_ta_tx_cb(void *priv, ubit_t *buf, size_t buf_size)
+{
+ const struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ if (state->sock != NULL && buf_size > 0) {
+ struct msgb *msg = msgb_alloc(buf_size, __func__);
+
+ tch_csd_sock_recv(state->sock, msg);
+ if (msgb_length(msg) < buf_size) {
+ LOGP(DCSD, LOGL_NOTICE,
+ "%s(): not enough bytes for sync Tx (%u < %zu)\n",
+ __func__, msgb_length(msg), buf_size);
+ }
+ }
+}
+
+static void tch_v110_ta_async_rx_cb(void *priv, const ubit_t *buf, size_t buf_size)
+{
+ const struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ osmo_soft_uart_rx_ubits(state->suart, buf, buf_size);
+}
+
+static void tch_v110_ta_async_tx_cb(void *priv, ubit_t *buf, size_t buf_size)
+{
+ const struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ osmo_soft_uart_tx_ubits(state->suart, buf, buf_size);
+}
+
+static const struct {
+ enum osmo_v110_ta_circuit c;
+ enum osmo_soft_uart_status s;
+} tch_v110_circuit_map[] = {
+ { OSMO_V110_TA_C_106, OSMO_SUART_STATUS_F_CTS },
+ { OSMO_V110_TA_C_107, OSMO_SUART_STATUS_F_DSR },
+ { OSMO_V110_TA_C_109, OSMO_SUART_STATUS_F_DCD },
+};
+
+static void tch_v110_ta_status_update_cb(void *priv, unsigned int status)
+{
+ const struct tch_data_state *state = (struct tch_data_state *)priv;
+
+ LOGP(DCSD, LOGL_DEBUG, "V.110 TA status mask=0x%08x\n", status);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(tch_v110_circuit_map); i++) {
+ enum osmo_v110_ta_circuit c = tch_v110_circuit_map[i].c;
+ enum osmo_soft_uart_status s = tch_v110_circuit_map[i].s;
+ bool is_on = (status & (1 << c)) != 0;
+
+ LOGP(DCSD, LOGL_DEBUG, "V.110 TA circuit %s (%s) is %s\n",
+ osmo_v110_ta_circuit_name(c),
+ osmo_v110_ta_circuit_desc(c),
+ is_on ? "ON" : "OFF");
+
+ /* update status lines of the soft-UART */
+ if (state->suart != NULL)
+ osmo_soft_uart_set_status_line(state->suart, s, is_on);
+ }
+}
+
+struct osmo_v110_ta *tch_v110_ta_alloc(struct osmocom_ms *ms,
+ const struct gsm_mncc_bearer_cap *bcap)
+{
+ struct tch_data_state *state = &ms->tch_state->data;
+
+ struct osmo_v110_ta_cfg cfg = {
+ /* .rate is set below */
+ .priv = (void *)state,
+ .rx_cb = &tch_v110_ta_rx_cb,
+ .tx_cb = &tch_v110_ta_tx_cb,
+ .status_update_cb = &tch_v110_ta_status_update_cb,
+ };
+
+ if (bcap->data.async) {
+ OSMO_ASSERT(state->suart != NULL);
+ cfg.rx_cb = &tch_v110_ta_async_rx_cb;
+ cfg.tx_cb = &tch_v110_ta_async_tx_cb;
+ }
+
+#define BCAP_RATE(interm_rate, user_rate) \
+ ((interm_rate << 8) | (user_rate << 0))
+
+ switch (BCAP_RATE(bcap->data.interm_rate, bcap->data.user_rate)) {
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_1200):
+ cfg.rate = OSMO_V110_SYNC_RA1_1200;
+ break;
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_2400):
+ cfg.rate = OSMO_V110_SYNC_RA1_2400;
+ break;
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_4800):
+ cfg.rate = OSMO_V110_SYNC_RA1_4800;
+ break;
+ case BCAP_RATE(GSM48_BCAP_IR_16k, GSM48_BCAP_UR_9600):
+ cfg.rate = OSMO_V110_SYNC_RA1_9600;
+ break;
+ /* TODO: according to 3GPP TS 44.021, section 4.1, the 300 bit/s user data
+ * signalling rate shall be adapted to a synchronous 600 bit/s stream. */
+ case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_300):
+ default:
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): IR 0x%02x / UR 0x%02x combination is not supported\n",
+ __func__, bcap->data.interm_rate, bcap->data.user_rate);
+ return NULL;
+ }
+
+#undef BCAP_RATE
+
+ osmo_v110_e1e2e3_set(state->e1e2e3, cfg.rate);
+
+ return osmo_v110_ta_alloc(ms, "csd_v110_ta", &cfg);
+}
+
+/*************************************************************************************/
+
+static void swap_words(uint8_t *data, size_t data_len)
+{
+ /* swap bytes in words */
+ while (data_len >= 2) {
+ uint8_t tmp = data[0];
+ data[0] = data[1];
+ data[1] = tmp;
+ data_len -= 2;
+ data += 2;
+ }
+}
+
+static int tch_csd_rx_from_l1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct tch_data_state *state = &ms->tch_state->data;
+ const struct gsm48_rr_cd *cd = &ms->rrlayer.cd_now;
+ const struct csd_v110_frame_desc *desc;
+ ubit_t data[4 * 60];
+ size_t data_len;
+
+ if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs)
+ desc = &csd_v110_lchan_desc[cd->mode].fr;
+ else /* RSL_CHAN_Lm_ACCHs */
+ desc = &csd_v110_lchan_desc[cd->mode].hr;
+ if (OSMO_UNLIKELY(desc->num_blocks == 0))
+ return -ENOTSUP;
+
+ data_len = desc->num_blocks * desc->num_bits;
+ OSMO_ASSERT(sizeof(data) >= data_len);
+
+ switch (ms->settings.tch_data.io_format) {
+ case TCH_DATA_IOF_OSMO:
+ /* trxcon emits raw bits from the convolutional decoder */
+ if (OSMO_UNLIKELY(msgb_l3len(msg) != data_len))
+ return -EINVAL;
+ memcpy(&data[0], msgb_l3(msg), msgb_l3len(msg));
+ break;
+ case TCH_DATA_IOF_TI:
+ /* the layer1 firmware emits packed bits (LE ordering) */
+ if (OSMO_UNLIKELY(msgb_l3len(msg) < data_len / 8))
+ return -EINVAL;
+ /* ... with swapped words (LE ordering) */
+ swap_words(msgb_l3(msg), msgb_l3len(msg));
+ osmo_pbit2ubit_ext(data, 0, msgb_l3(msg), 0, data_len, 1);
+ break;
+ default:
+ LOGP(DCSD, LOGL_FATAL,
+ "%s(): unhandled data I/O format\n", __func__);
+ OSMO_ASSERT(0);
+ }
+
+ for (unsigned int i = 0; i < desc->num_blocks; i++) {
+ struct osmo_v110_decoded_frame df;
+
+ if (desc->num_bits == 60)
+ osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60);
+ else /* desc->num_bits == 36 */
+ osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36);
+
+ /* E1/E2/E3 is out-of-band knowledge in GSM/CSD */
+ memcpy(df.e_bits, state->e1e2e3, sizeof(state->e1e2e3));
+
+ osmo_v110_ta_frame_in(state->v110_ta, &df);
+ }
+
+ if (state->suart != NULL)
+ osmo_soft_uart_flush_rx(state->suart);
+
+ return 0;
+}
+
+static int tch_csd_tx_to_l1(struct osmocom_ms *ms)
+{
+ struct tch_data_state *state = &ms->tch_state->data;
+ const struct gsm48_rr_cd *cd = &ms->rrlayer.cd_now;
+ const struct csd_v110_frame_desc *desc;
+ ubit_t data[60 * 4];
+ struct msgb *nmsg;
+ size_t data_len;
+
+ if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs)
+ desc = &csd_v110_lchan_desc[cd->mode].fr;
+ else /* RSL_CHAN_Lm_ACCHs */
+ desc = &csd_v110_lchan_desc[cd->mode].hr;
+ if (OSMO_UNLIKELY(desc->num_blocks == 0))
+ return -ENOTSUP;
+
+ data_len = desc->num_blocks * desc->num_bits;
+ OSMO_ASSERT(sizeof(data) >= data_len);
+
+ for (unsigned int i = 0; i < desc->num_blocks; i++) {
+ struct osmo_v110_decoded_frame df;
+
+ if (osmo_v110_ta_frame_out(state->v110_ta, &df) != 0)
+ memset(&df, 0x01, sizeof(df));
+
+ /* If E1/E2/E3 bits indicate a meaningful user data rate (see Table 5/V.110),
+ * set E7 to binary 0 in every 4-th frame (as per 3GPP TS 44.021, subclause 10.2.1).
+ * ITU-T V.110 requires this only for 600 bps, but 3GPP TS 44.021 clearly states
+ * that "such a multiframe structure exists for all user data rates". */
+ if ((df.e_bits[0] + df.e_bits[1] + df.e_bits[2]) == 2)
+ df.e_bits[6] = (state->num_tx != 0);
+ state->num_tx = (state->num_tx + 1) & 0x03;
+
+ if (desc->num_bits == 60)
+ osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df);
+ else /* desc->num_bits == 36 */
+ osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df);
+ }
+
+ switch (ms->settings.tch_data.io_format) {
+ case TCH_DATA_IOF_OSMO:
+ /* trxcon operates on unpacked bits */
+ nmsg = msgb_alloc_headroom(data_len + 64, 64, __func__);
+ if (nmsg == NULL)
+ return -ENOMEM;
+ memcpy(msgb_put(nmsg, data_len), &data[0], data_len);
+ break;
+ case TCH_DATA_IOF_TI:
+ /* XXX: the layer1 firmware expects TRAFFIC.req with len=33 bytes */
+ nmsg = msgb_alloc_headroom(33 + 64, 64, __func__);
+ if (nmsg == NULL)
+ return -ENOMEM;
+ nmsg->l2h = msgb_put(nmsg, 33);
+ /* the layer1 firmware expects packed bits (LE ordering) */
+ osmo_ubit2pbit_ext(msgb_l2(nmsg), 0, &data[0], 0, sizeof(data), 1);
+ /* ... with swapped words (LE ordering) */
+ swap_words(msgb_l2(nmsg), msgb_l2len(nmsg));
+ break;
+ default:
+ LOGP(DCSD, LOGL_FATAL,
+ "%s(): unhandled data I/O format\n", __func__);
+ OSMO_ASSERT(0);
+ }
+
+ return tch_send_msg(ms, nmsg);
+}
+
+static int tch_data_check_bcap(const struct gsm_mncc_bearer_cap *bcap)
+{
+ if (bcap == NULL) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): CC transaction without BCap\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (bcap->mode != GSM48_BCAP_TMOD_CIRCUIT) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): Transfer mode 0x%02x is not supported\n",
+ __func__, bcap->mode);
+ return -ENOTSUP;
+ }
+ if (bcap->coding != GSM48_BCAP_CODING_GSM_STD) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): Coding standard 0x%02x is not supported\n",
+ __func__, bcap->coding);
+ return -ENOTSUP;
+ }
+
+ switch (bcap->transfer) {
+ case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ if (bcap->data.rate_adaption != GSM48_BCAP_RA_V110_X30) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): Rate adaption (octet 5) 0x%02x is not supported\n",
+ __func__, bcap->data.rate_adaption);
+ return -ENOTSUP;
+ }
+ break;
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
+ case GSM48_BCAP_ITCAP_FAX_G3:
+ if (bcap->data.rate_adaption != GSM48_BCAP_RA_NONE) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): Rate adaption (octet 5) 0x%02x was expected to be NONE\n",
+ __func__, bcap->data.rate_adaption);
+ return -ENOTSUP;
+ }
+ break;
+ default:
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): Information transfer capability 0x%02x is not supported\n",
+ __func__, bcap->transfer);
+ return -ENOTSUP;
+ }
+
+ if (bcap->data.sig_access != GSM48_BCAP_SA_I440_I450) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): Signalling access protocol (octet 5) 0x%02x is not supported\n",
+ __func__, bcap->data.sig_access);
+ return -ENOTSUP;
+ }
+ if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) {
+ LOGP(DCSD, LOGL_ERROR,
+ "%s(): only transparent calls are supported so far\n",
+ __func__);
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
+/*************************************************************************************/
+
+int tch_data_recv(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct tch_data_state *state = &ms->tch_state->data;
+
+ switch (state->handler) {
+ case TCH_DATA_IOH_LOOPBACK:
+ /* Remove the DL info header */
+ msgb_pull_to_l2(msg);
+ /* Send data frame back */
+ return tch_send_msg(ms, msg);
+ case TCH_DATA_IOH_UNIX_SOCK:
+ tch_csd_rx_from_l1(ms, msg);
+ tch_csd_tx_to_l1(ms);
+ msgb_free(msg);
+ break;
+ case TCH_DATA_IOH_NONE:
+ /* Drop voice frame */
+ msgb_free(msg);
+ break;
+ }
+
+ return 0;
+}
+
+int tch_data_state_init(struct gsm_trans *trans,
+ struct tch_data_state *state)
+{
+ struct osmocom_ms *ms = trans->ms;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ const struct gsm_mncc_bearer_cap *bcap = trans->cc.bcap;
+ int rc;
+
+ if ((rc = tch_data_check_bcap(bcap)) != 0)
+ return rc;
+
+ switch (state->handler) {
+ case TCH_DATA_IOH_UNIX_SOCK:
+ state->sock = tch_csd_sock_init(ms);
+ if (state->sock == NULL)
+ return -ENOMEM;
+ rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ;
+ break;
+ case TCH_DATA_IOH_LOOPBACK:
+ case TCH_DATA_IOH_NONE:
+ rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ;
+ /* we don't need V.110 TA / soft-UART */
+ return 0;
+ }
+
+ if (bcap->data.async) {
+ state->suart = tch_soft_uart_alloc(ms, bcap);
+ if (state->suart == NULL)
+ goto exit_free;
+ }
+
+ state->v110_ta = tch_v110_ta_alloc(ms, bcap);
+ if (state->v110_ta == NULL)
+ goto exit_free;
+
+ return 0;
+
+exit_free:
+ if (state->sock != NULL)
+ tch_csd_sock_exit(state->sock);
+ if (state->suart != NULL)
+ osmo_soft_uart_free(state->suart);
+ if (state->v110_ta != NULL)
+ osmo_v110_ta_free(state->v110_ta);
+ return -1;
+}
+
+void tch_data_state_free(struct tch_data_state *state)
+{
+ switch (state->handler) {
+ case TCH_DATA_IOH_UNIX_SOCK:
+ if (state->sock != NULL)
+ tch_csd_sock_exit(state->sock);
+ break;
+ default:
+ break;
+ }
+
+ if (state->suart != NULL)
+ osmo_soft_uart_free(state->suart);
+ if (state->v110_ta != NULL)
+ osmo_v110_ta_free(state->v110_ta);
+}
+
+void tch_csd_sock_state_cb(struct osmocom_ms *ms, bool connected)
+{
+ struct tch_data_state *state = NULL;
+
+ if (ms->tch_state == NULL || ms->tch_state->is_voice) {
+ LOGP(DCSD, LOGL_INFO, "No data call is ongoing, "
+ "ignoring [dis]connection event for CSD socket\n");
+ return;
+ }
+
+ state = &ms->tch_state->data;
+ osmo_v110_ta_set_circuit(state->v110_ta, OSMO_V110_TA_C_108, connected);
+
+ /* GSM/CSD employs the modified 60-bit V.110 frame format, which is basically
+ * a stripped down version of the nurmal 80-bit V.110 frame without E1/E2/E3
+ * bits and without the sync pattern. These 60-bit V.110 frames are perfectly
+ * aligned with the radio interface block boundaries, so we're always in sync. */
+ if (connected)
+ osmo_v110_ta_sync_ind(state->v110_ta);
+}
diff --git a/src/host/layer23/src/mobile/tch_data_sock.c b/src/host/layer23/src/mobile/tch_data_sock.c
new file mode 100644
index 00000000..7ce5a4f1
--- /dev/null
+++ b/src/host/layer23/src/mobile/tch_data_sock.c
@@ -0,0 +1,243 @@
+/*
+ * (C) 2023 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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/mobile/tch.h>
+
+struct tch_csd_sock_state {
+ struct osmocom_ms *ms; /* the MS instance we belong to */
+ struct osmo_fd listen_bfd; /* fd for listen socket */
+ struct osmo_fd conn_bfd; /* fd for a client connection */
+ struct llist_head rxqueue;
+ struct llist_head txqueue;
+};
+
+void tch_csd_sock_state_cb(struct osmocom_ms *ms, bool connected);
+
+static void tch_csd_sock_close(struct tch_csd_sock_state *state)
+{
+ struct osmo_fd *bfd = &state->conn_bfd;
+
+ LOGP(DCSD, LOGL_NOTICE, "TCH CSD sock has closed connection\n");
+
+ tch_csd_sock_state_cb(state->ms, false);
+
+ 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);
+
+ /* flush the queues */
+ while (!llist_empty(&state->rxqueue))
+ msgb_free(msgb_dequeue(&state->rxqueue));
+ while (!llist_empty(&state->txqueue))
+ msgb_free(msgb_dequeue(&state->txqueue));
+}
+
+static int tch_csd_sock_read(struct osmo_fd *bfd)
+{
+ struct tch_csd_sock_state *state = (struct tch_csd_sock_state *)bfd->data;
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc(256, "tch_csd_sock_rx");
+ if (!msg)
+ return -ENOMEM;
+
+ rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
+ if (rc == 0)
+ goto close;
+ if (rc < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ goto close;
+ }
+
+ msgb_put(msg, rc);
+ msgb_enqueue(&state->rxqueue, msg);
+ return rc;
+
+close:
+ msgb_free(msg);
+ tch_csd_sock_close(state);
+ return -1;
+}
+
+static int tch_csd_sock_write(struct osmo_fd *bfd)
+{
+ struct tch_csd_sock_state *state = bfd->data;
+
+ while (!llist_empty(&state->txqueue)) {
+ struct msgb *msg;
+ int rc;
+
+ /* dequeue a msgb */
+ msg = msgb_dequeue(&state->txqueue);
+
+ /* try to send it over the socket */
+ rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
+ if (rc < 0 && errno == EAGAIN) {
+ llist_add(&msg->list, &state->txqueue);
+ return 0;
+ }
+ msgb_free(msg);
+ if (rc <= 0)
+ goto close;
+ }
+
+ osmo_fd_write_disable(bfd);
+ return 0;
+
+close:
+ tch_csd_sock_close(state);
+ return -1;
+}
+
+static int tch_csd_sock_cb(struct osmo_fd *bfd, unsigned int flags)
+{
+ int rc = 0;
+
+ if (flags & OSMO_FD_READ) {
+ rc = tch_csd_sock_read(bfd);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (flags & OSMO_FD_WRITE)
+ rc = tch_csd_sock_write(bfd);
+
+ return rc;
+}
+
+static int tch_csd_sock_accept(struct osmo_fd *bfd, unsigned int flags)
+{
+ struct tch_csd_sock_state *state = (struct tch_csd_sock_state *)bfd->data;
+ struct osmo_fd *conn_bfd = &state->conn_bfd;
+ struct sockaddr_un un_addr;
+ socklen_t len;
+ int rc;
+
+ len = sizeof(un_addr);
+ rc = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
+ if (rc < 0) {
+ LOGP(DCSD, LOGL_ERROR, "Failed to accept() a new connection\n");
+ return -1;
+ }
+
+ if (conn_bfd->fd >= 0) {
+ LOGP(DCSD, LOGL_NOTICE, "TCH CSD sock already has an active connection\n");
+ close(rc); /* reject this connection request */
+ return 0;
+ }
+
+ osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, &tch_csd_sock_cb, state, 0);
+ if (osmo_fd_register(conn_bfd) != 0) {
+ LOGP(DCSD, LOGL_ERROR, "osmo_fd_register() failed\n");
+ close(conn_bfd->fd);
+ conn_bfd->fd = -1;
+ return -1;
+ }
+
+ LOGP(DCSD, LOGL_NOTICE, "TCH CSD sock got a connection\n");
+
+ tch_csd_sock_state_cb(state->ms, true);
+
+ return 0;
+}
+
+struct tch_csd_sock_state *tch_csd_sock_init(struct osmocom_ms *ms)
+{
+ const char *sock_path = ms->settings.tch_data.unix_socket_path;
+ struct tch_csd_sock_state *state;
+ struct osmo_fd *bfd;
+ int rc;
+
+ state = talloc_zero(ms, struct tch_csd_sock_state);
+ if (state == NULL)
+ return NULL;
+
+ INIT_LLIST_HEAD(&state->rxqueue);
+ INIT_LLIST_HEAD(&state->txqueue);
+ state->conn_bfd.fd = -1;
+ state->ms = ms;
+
+ bfd = &state->listen_bfd;
+
+ rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
+ LOGP(DCSD, LOGL_ERROR, "Could not create unix socket: %s\n", strerror(errno));
+ talloc_free(state);
+ return NULL;
+ }
+
+ bfd->cb = &tch_csd_sock_accept;
+ bfd->data = state;
+
+ return state;
+}
+
+void tch_csd_sock_exit(struct tch_csd_sock_state *state)
+{
+ if (state->conn_bfd.fd > -1)
+ tch_csd_sock_close(state);
+ osmo_fd_unregister(&state->listen_bfd);
+ close(state->listen_bfd.fd);
+ talloc_free(state);
+}
+
+void tch_csd_sock_recv(struct tch_csd_sock_state *state, struct msgb *msg)
+{
+ while (msgb_tailroom(msg) > 0) {
+ struct msgb *rmsg = msgb_dequeue(&state->rxqueue);
+ if (rmsg == NULL)
+ break;
+ size_t len = OSMO_MIN(msgb_tailroom(msg), msgb_length(rmsg));
+ memcpy(msgb_put(msg, len), msgb_data(rmsg), len);
+ msgb_pull(rmsg, len);
+ if (msgb_length(rmsg) > 0)
+ llist_add(&rmsg->list, &state->rxqueue);
+ else
+ msgb_free(rmsg);
+ }
+}
+
+void tch_csd_sock_send(struct tch_csd_sock_state *state, struct msgb *msg)
+{
+ msgb_enqueue(&state->txqueue, msg);
+ osmo_fd_write_enable(&state->conn_bfd);
+}
diff --git a/src/host/layer23/src/mobile/tch_voice.c b/src/host/layer23/src/mobile/tch_voice.c
new file mode 100644
index 00000000..ac793e84
--- /dev/null
+++ b/src/host/layer23/src/mobile/tch_voice.c
@@ -0,0 +1,155 @@
+/*
+ * (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2022-2024 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 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.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/mobile/gapk_io.h>
+#include <osmocom/bb/mobile/mncc.h>
+#include <osmocom/bb/mobile/mncc_sock.h>
+#include <osmocom/bb/mobile/transaction.h>
+#include <osmocom/bb/mobile/tch.h>
+
+#include <l1ctl_proto.h>
+
+/* Forward a Downlink voice frame to the external MNCC handler */
+static int tch_forward_mncc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_data_frame *mncc;
+
+ /* Drop the l1ctl_info_dl header */
+ msgb_pull_to_l2(msg);
+ /* push mncc header in front of data */
+ mncc = (struct gsm_data_frame *)
+ msgb_push(msg, sizeof(struct gsm_data_frame));
+ mncc->callref = ms->mncc_entity.ref;
+
+ switch (ms->rrlayer.cd_now.mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ {
+ const uint8_t cbits = ms->rrlayer.cd_now.chan_nr >> 3;
+ if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
+ mncc->msg_type = GSM_TCHF_FRAME;
+ else
+ mncc->msg_type = GSM_TCHH_FRAME;
+ break;
+ }
+ case GSM48_CMODE_SPEECH_EFR:
+ mncc->msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* TODO: no AMR support yet */
+ default:
+ /* TODO: print error message here */
+ goto exit_free;
+ }
+
+ /* distribute and then free */
+ if (ms->mncc_entity.sock_state && ms->mncc_entity.ref)
+ return mncc_sock_from_cc(ms->mncc_entity.sock_state, msg);
+
+exit_free:
+ msgb_free(msg);
+ return 0;
+}
+
+int tch_voice_recv(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct tch_voice_state *state = &ms->tch_state->voice;
+
+ switch (state->handler) {
+ case TCH_VOICE_IOH_LOOPBACK:
+ /* Remove the DL info header */
+ msgb_pull_to_l2(msg);
+ /* Send voice frame back */
+ return tch_send_msg(ms, msg);
+ case TCH_VOICE_IOH_MNCC_SOCK:
+ return tch_forward_mncc(ms, msg);
+ case TCH_VOICE_IOH_GAPK:
+#ifdef WITH_GAPK_IO
+ if (state->gapk_io != NULL) {
+ gapk_io_enqueue_dl(state->gapk_io, msg);
+ gapk_io_dequeue_ul(ms, state->gapk_io);
+ } else {
+ msgb_free(msg);
+ }
+ break;
+#endif
+ case TCH_VOICE_IOH_L1PHY:
+ case TCH_VOICE_IOH_NONE:
+ /* Drop voice frame */
+ msgb_free(msg);
+ break;
+ }
+
+ return 0;
+}
+
+int tch_voice_state_init(struct gsm_trans *trans, struct tch_voice_state *state)
+{
+ struct osmocom_ms *ms = trans->ms;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ const struct gsm48_rr_cd *cd = &rr->cd_now;
+
+ switch (state->handler) {
+ case TCH_VOICE_IOH_L1PHY:
+ rr->audio_mode = AUDIO_RX_SPEAKER | AUDIO_TX_MICROPHONE;
+ break;
+ case TCH_VOICE_IOH_MNCC_SOCK:
+ case TCH_VOICE_IOH_LOOPBACK:
+ rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ;
+ break;
+#ifdef WITH_GAPK_IO
+ case TCH_VOICE_IOH_GAPK:
+ if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs)
+ state->gapk_io = gapk_io_state_alloc_mode_rate(ms, cd->mode, true);
+ else /* RSL_CHAN_Lm_ACCHs */
+ state->gapk_io = gapk_io_state_alloc_mode_rate(ms, cd->mode, false);
+ if (state->gapk_io == NULL)
+ return -1;
+ rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ;
+ break;
+#endif
+ case TCH_VOICE_IOH_NONE:
+ rr->audio_mode = 0x00;
+ break;
+ }
+
+ return 0;
+}
+
+void tch_voice_state_free(struct tch_voice_state *state)
+{
+ switch (state->handler) {
+#ifdef WITH_GAPK_IO
+ case TCH_VOICE_IOH_GAPK:
+ gapk_io_state_free(state->gapk_io);
+ break;
+#endif
+ default:
+ break;
+ }
+}
diff --git a/src/host/layer23/src/mobile/transaction.c b/src/host/layer23/src/mobile/transaction.c
index 9824bd1b..570545ba 100644
--- a/src/host/layer23/src/mobile/transaction.c
+++ b/src/host/layer23/src/mobile/transaction.c
@@ -13,19 +13,17 @@
* 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 <stdint.h>
+#include <osmocom/core/signal.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/msgb.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/transaction.h>
@@ -33,6 +31,7 @@
void _gsm48_cc_trans_free(struct gsm_trans *trans);
void _gsm480_ss_trans_free(struct gsm_trans *trans);
void _gsm411_sms_trans_free(struct gsm_trans *trans);
+void _gsm44068_gcc_bcc_trans_free(struct gsm_trans *trans);
struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms,
uint8_t proto, uint8_t trans_id)
@@ -47,13 +46,13 @@ struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms,
return NULL;
}
-struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms,
+struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, uint8_t protocol,
uint32_t callref)
{
struct gsm_trans *trans;
llist_for_each_entry(trans, &ms->trans_list, entry) {
- if (trans->callref == callref)
+ if (trans->protocol == protocol && trans->callref == callref)
return trans;
}
return NULL;
@@ -81,11 +80,15 @@ struct gsm_trans *trans_alloc(struct osmocom_ms *ms,
llist_add_tail(&trans->entry, &ms->trans_list);
+ osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_ALLOC, trans);
+
return trans;
}
void trans_free(struct gsm_trans *trans)
{
+ osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_FREE, trans);
+
switch (trans->protocol) {
case GSM48_PDISC_CC:
_gsm48_cc_trans_free(trans);
@@ -96,6 +99,10 @@ void trans_free(struct gsm_trans *trans)
case GSM48_PDISC_SMS:
_gsm411_sms_trans_free(trans);
break;
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ _gsm44068_gcc_bcc_trans_free(trans);
+ break;
}
DEBUGP(DCC, "ms %s frees transaction (mem %p)\n", trans->ms->name,
diff --git a/src/host/layer23/src/mobile/voice.c b/src/host/layer23/src/mobile/voice.c
deleted file mode 100644
index c3792650..00000000
--- a/src/host/layer23/src/mobile/voice.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
- *
- * 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
- * (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.
- *
- */
-
-#include <stdlib.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/codec/codec.h>
-
-#include <osmocom/bb/common/logging.h>
-#include <osmocom/bb/common/osmocom_data.h>
-#include <osmocom/bb/mobile/mncc.h>
-#include <osmocom/bb/mobile/voice.h>
-
-
-/*
- * receive voice
- */
-
-static int gsm_recv_voice(struct osmocom_ms *ms, struct msgb *msg)
-{
- struct gsm_data_frame *mncc;
-
- /* Drop the l1ctl_info_dl header */
- msgb_pull_to_l2(msg);
- /* push mncc header in front of data */
- mncc = (struct gsm_data_frame *)
- msgb_push(msg, sizeof(struct gsm_data_frame));
- mncc->callref = ms->mncc_entity.ref;
-
- /* FIXME: FR, EFR only! */
- switch (ms->rrlayer.cd_now.mode) {
- case GSM48_CMODE_SPEECH_V1:
- mncc->msg_type = GSM_TCHF_FRAME;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- mncc->msg_type = GSM_TCHF_FRAME_EFR;
- break;
- default:
- /* TODO: print error message here */
- goto exit_free;
- }
-
- /* send voice frame back, if appropriate */
- if (ms->settings.audio.io_handler == AUDIO_IOH_LOOPBACK)
- gsm_send_voice(ms, mncc);
-
- /* distribute and then free */
- if (ms->mncc_entity.mncc_recv && ms->mncc_entity.ref) {
- ms->mncc_entity.mncc_recv(ms, mncc->msg_type, mncc);
- }
-
-exit_free:
- msgb_free(msg);
- return 0;
-}
-
-/*
- * send voice
- */
-int gsm_send_voice(struct osmocom_ms *ms, struct gsm_data_frame *data)
-{
- struct msgb *nmsg;
- int len;
-
- switch (ms->rrlayer.cd_now.mode) {
- case GSM48_CMODE_SPEECH_V1:
- /* FIXME: FR only, check for TCH/F (FR) and TCH/H (HR) */
- len = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- len = GSM_EFR_BYTES;
- break;
- default:
- LOGP(DL1C, LOGL_ERROR, "gsm_send_voice, msg_type=0x%02x: not implemented\n", data->msg_type);
- return -EINVAL;
- }
-
- nmsg = msgb_alloc_headroom(len + 64, 64, "TCH/F");
- if (!nmsg)
- return -ENOMEM;
- nmsg->l2h = msgb_put(nmsg, len);
- memcpy(nmsg->l2h, data->data, len);
-
- return gsm48_rr_tx_voice(ms, nmsg);
-}
-
-/*
- * init
- */
-
-int gsm_voice_init(struct osmocom_ms *ms)
-{
- ms->l1_entity.l1_traffic_ind = gsm_recv_voice;
-
- return 0;
-}
diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c
index 073303ca..eb582d35 100644
--- a/src/host/layer23/src/mobile/vty_interface.c
+++ b/src/host/layer23/src/mobile/vty_interface.c
@@ -13,10 +13,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 <string.h>
@@ -30,8 +26,10 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/signal.h>
#include <osmocom/crypt/auth.h>
+#include <osmocom/gsm/gsm23003.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/gps.h>
#include <osmocom/bb/mobile/mncc.h>
@@ -41,55 +39,39 @@
#include <osmocom/bb/mobile/app_mobile.h>
#include <osmocom/bb/mobile/gsm480_ss.h>
#include <osmocom/bb/mobile/gsm411_sms.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
-extern struct llist_head ms_list;
-extern struct llist_head active_connections;
-
-struct cmd_node ms_node = {
- MS_NODE,
- "%s(ms)# ",
+struct cmd_node support_node = {
+ SUPPORT_NODE,
+ "%s(support)# ",
1
};
-struct cmd_node testsim_node = {
- TESTSIM_NODE,
- "%s(test-sim)# ",
+struct cmd_node tch_voice_node = {
+ TCH_VOICE_NODE,
+ "%s(tch-voice)# ",
1
};
-struct cmd_node support_node = {
- SUPPORT_NODE,
- "%s(support)# ",
+struct cmd_node tch_data_node = {
+ TCH_DATA_NODE,
+ "%s(tch-data)# ",
1
};
-struct cmd_node audio_node = {
- AUDIO_NODE,
- "%s(audio)# ",
+struct cmd_node vgcs_node = {
+ VGCS_NODE,
+ "%s(group-call)# ",
1
};
-static void print_vty(void *priv, const char *fmt, ...)
-{
- char buffer[1000];
- struct vty *vty = priv;
- va_list args;
-
- va_start(args, fmt);
- vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
- buffer[sizeof(buffer) - 1] = '\0';
- va_end(args);
-
- if (buffer[0]) {
- if (buffer[strlen(buffer) - 1] == '\n') {
- buffer[strlen(buffer) - 1] = '\0';
- vty_out(vty, "%s%s", buffer, VTY_NEWLINE);
- } else
- vty_out(vty, "%s", buffer);
- }
-}
+struct cmd_node vbs_node = {
+ VBS_NODE,
+ "%s(broadcast-call)# ",
+ 1
+};
int vty_check_number(struct vty *vty, const char *number)
{
@@ -116,12 +98,35 @@ int vty_check_number(struct vty *vty, const char *number)
return 0;
}
-int vty_reading = 0;
-static int hide_default = 0;
+int vty_check_callref(struct vty *vty, const char *number)
+{
+ int i, ii = strlen(number);
+
+ /* First check digits, so that a false command result the following error message. */
+ for (i = 0; i < ii; i++) {
+ if (!(number[i] >= '0' && number[i] <= '9')) {
+ vty_out(vty, "Invalid digit '%c' in callref!%s",
+ number[i], VTY_NEWLINE);
+ return -EINVAL;
+ }
+ }
+
+ if (ii < 1) {
+ vty_out(vty, "Given callref has no digits!%s", VTY_NEWLINE);
+ return -EINVAL;
+ }
+
+ if (ii > 8) {
+ vty_out(vty, "Given callref is too long!%s", VTY_NEWLINE);
+ return -EINVAL;
+ }
+
+ return 0;
+}
static void vty_restart(struct vty *vty, struct osmocom_ms *ms)
{
- if (vty_reading)
+ if (l23_vty_reading)
return;
if (ms->shutdown != MS_SHUTDOWN_NONE)
return;
@@ -136,25 +141,6 @@ static void vty_restart_if_started(struct vty *vty, struct osmocom_ms *ms)
vty_restart(vty, ms);
}
-static struct osmocom_ms *get_ms(const char *name, struct vty *vty)
-{
- struct osmocom_ms *ms;
-
- llist_for_each_entry(ms, &ms_list, entity) {
- if (!strcmp(ms->name, name)) {
- if (ms->shutdown != MS_SHUTDOWN_NONE) {
- vty_out(vty, "MS '%s' is admin down.%s", name,
- VTY_NEWLINE);
- return NULL;
- }
- return ms;
- }
- }
- vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE);
-
- return NULL;
-}
-
static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty)
{
struct gsm_settings *set = &ms->settings;
@@ -210,23 +196,21 @@ static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty)
else
vty_out(vty, " manual network selection state : %s%s",
get_m_state_name(ms->plmn.state), VTY_NEWLINE);
- if (ms->plmn.mcc)
+ if (ms->plmn.plmn.mcc)
vty_out(vty, " MCC=%s "
- "MNC=%s (%s, %s)%s", gsm_print_mcc(ms->plmn.mcc),
- gsm_print_mnc(ms->plmn.mnc), gsm_get_mcc(ms->plmn.mcc),
- gsm_get_mnc(ms->plmn.mcc, ms->plmn.mnc), VTY_NEWLINE);
+ "MNC=%s (%s, %s)%s", osmo_mcc_name(ms->plmn.plmn.mcc),
+ osmo_mnc_name(ms->plmn.plmn.mnc, ms->plmn.plmn.mnc_3_digits),
+ gsm_get_mcc(ms->plmn.plmn.mcc),
+ gsm_get_mnc(&ms->plmn.plmn), VTY_NEWLINE);
vty_out(vty, " cell selection state: %s%s",
get_cs_state_name(ms->cellsel.state), VTY_NEWLINE);
- if (ms->cellsel.sel_mcc) {
- vty_out(vty, " ARFCN=%s MCC=%s MNC=%s "
- "LAC=0x%04x CELLID=0x%04x%s",
+ if (ms->cellsel.sel_cgi.lai.plmn.mcc) {
+ vty_out(vty, " ARFCN=%s CGI=%s%s",
gsm_print_arfcn(ms->cellsel.sel_arfcn),
- gsm_print_mcc(ms->cellsel.sel_mcc),
- gsm_print_mnc(ms->cellsel.sel_mnc),
- ms->cellsel.sel_lac, ms->cellsel.sel_id, VTY_NEWLINE);
+ osmo_cgi_name(&ms->cellsel.sel_cgi), VTY_NEWLINE);
vty_out(vty, " (%s, %s)%s",
- gsm_get_mcc(ms->cellsel.sel_mcc),
- gsm_get_mnc(ms->cellsel.sel_mcc, ms->cellsel.sel_mnc),
+ gsm_get_mcc(ms->cellsel.sel_cgi.lai.plmn.mcc),
+ gsm_get_mnc(&ms->cellsel.sel_cgi.lai.plmn),
VTY_NEWLINE);
}
vty_out(vty, " radio resource layer state: %s%s",
@@ -245,7 +229,8 @@ static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty)
DEFUN(show_ms, show_ms_cmd, "show ms [MS_NAME]",
- SHOW_STR "Display available MS entities\n")
+ SHOW_STR "Display available MS entities\n"
+ "Display specific MS with given name")
{
struct osmocom_ms *ms;
@@ -257,57 +242,13 @@ DEFUN(show_ms, show_ms_cmd, "show ms [MS_NAME]",
}
}
vty_out(vty, "MS name '%s' does not exits.%s", argv[0],
- VTY_NEWLINE);
+ VTY_NEWLINE);
return CMD_WARNING;
- } else {
- llist_for_each_entry(ms, &ms_list, entity) {
- gsm_ms_dump(ms, vty);
- vty_out(vty, "%s", VTY_NEWLINE);
- }
}
- return CMD_SUCCESS;
-}
-
-DEFUN(show_support, show_support_cmd, "show support [MS_NAME]",
- SHOW_STR "Display information about MS support\n"
- "Name of MS (see \"show ms\")")
-{
- struct osmocom_ms *ms;
-
- if (argc) {
- ms = get_ms(argv[0], vty);
- if (!ms)
- return CMD_WARNING;
- gsm_support_dump(ms, print_vty, vty);
- } else {
- llist_for_each_entry(ms, &ms_list, entity) {
- gsm_support_dump(ms, print_vty, vty);
- vty_out(vty, "%s", VTY_NEWLINE);
- }
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_subscr, show_subscr_cmd, "show subscriber [MS_NAME]",
- SHOW_STR "Display information about subscriber\n"
- "Name of MS (see \"show ms\")")
-{
- struct osmocom_ms *ms;
-
- if (argc) {
- ms = get_ms(argv[0], vty);
- if (!ms)
- return CMD_WARNING;
- gsm_subscr_dump(&ms->subscr, print_vty, vty);
- } else {
- llist_for_each_entry(ms, &ms_list, entity) {
- if (ms->shutdown == MS_SHUTDOWN_NONE) {
- gsm_subscr_dump(&ms->subscr, print_vty, vty);
- vty_out(vty, "%s", VTY_NEWLINE);
- }
- }
+ llist_for_each_entry(ms, &ms_list, entity) {
+ gsm_ms_dump(ms, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
}
return CMD_SUCCESS;
@@ -319,11 +260,11 @@ DEFUN(show_cell, show_cell_cmd, "show cell MS_NAME",
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SUPPORT, print_vty,
+ gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SUPPORT, l23_vty_printf,
vty);
return CMD_SUCCESS;
@@ -338,7 +279,7 @@ DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023> [pcs]",
struct gsm48_sysinfo *s;
uint16_t arfcn = atoi(argv[1]);
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
@@ -358,7 +299,7 @@ DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023> [pcs]",
return CMD_SUCCESS;
}
- gsm48_sysinfo_dump(s, arfcn, print_vty, vty, ms->settings.freq_map);
+ gsm48_sysinfo_dump(s, arfcn, l23_vty_printf, vty, ms->settings.freq_map);
return CMD_SUCCESS;
}
@@ -369,11 +310,11 @@ DEFUN(show_nbcells, show_nbcells_cmd, "show neighbour-cells MS_NAME",
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- gsm322_dump_nb_list(&ms->cellsel, print_vty, vty);
+ gsm322_dump_nb_list(&ms->cellsel, l23_vty_printf, vty);
return CMD_SUCCESS;
}
@@ -384,26 +325,26 @@ DEFUN(show_ba, show_ba_cmd, "show ba MS_NAME [MCC] [MNC]",
"Mobile Network Code")
{
struct osmocom_ms *ms;
- uint16_t mcc = 0, mnc = 0;
+ struct osmo_plmn_id plmn;
+ struct osmo_plmn_id *plmn_ptr = NULL;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
if (argc >= 3) {
- mcc = gsm_input_mcc((char *)argv[1]);
- mnc = gsm_input_mnc((char *)argv[2]);
- if (mcc == GSM_INPUT_INVALID) {
+ if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) {
vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (mnc == GSM_INPUT_INVALID) {
+ if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
return CMD_WARNING;
}
+ plmn_ptr = &plmn;
}
- gsm322_dump_ba_list(&ms->cellsel, mcc, mnc, print_vty, vty);
+ gsm322_dump_ba_list(&ms->cellsel, plmn_ptr, l23_vty_printf, vty);
return CMD_SUCCESS;
}
@@ -414,11 +355,11 @@ DEFUN(show_forb_plmn, show_forb_plmn_cmd, "show forbidden plmn MS_NAME",
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- gsm_subscr_dump_forbidden_plmn(ms, print_vty, vty);
+ gsm_subscr_dump_forbidden_plmn(ms, l23_vty_printf, vty);
return CMD_SUCCESS;
}
@@ -429,11 +370,41 @@ DEFUN(show_forb_la, show_forb_la_cmd, "show forbidden location-area MS_NAME",
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- gsm322_dump_forbidden_la(ms, print_vty, vty);
+ gsm322_dump_forbidden_la(ms, l23_vty_printf, vty);
+
+ return CMD_SUCCESS;
+}
+
+#define SHOW_ASCI_STR SHOW_STR "Display information about ASCI items\nName of MS (see \"show ms\")\n"
+
+DEFUN(show_asci_calls, show_asci_calls_cmd, "show asci MS_NAME calls",
+ SHOW_ASCI_STR "Display ongoing ASCI calls")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ gsm44068_dump_calls(ms, l23_vty_printf, vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_asci_neighbors, show_asci_neighbors_cmd, "show asci MS_NAME neighbors",
+ SHOW_ASCI_STR "Display neigbor cells of ongoing or last ASCI call")
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ gsm48_si10_dump(ms->cellsel.si, l23_vty_printf, vty);
return CMD_SUCCESS;
}
@@ -443,7 +414,7 @@ DEFUN(monitor_network, monitor_network_cmd, "monitor network MS_NAME",
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
@@ -458,7 +429,7 @@ DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME",
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
@@ -467,440 +438,482 @@ DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME",
return CMD_SUCCESS;
}
-static int _sim_test_cmd(struct vty *vty, int argc, const char *argv[],
- int attached)
+DEFUN(network_select, network_select_cmd,
+ "network select MS_NAME MCC MNC [force]",
+ "Select ...\nSelect Network\nName of MS (see \"show ms\")\n"
+ "Mobile Country Code\nMobile Network Code\n"
+ "Force selecting a network that is not in the list")
{
struct osmocom_ms *ms;
- struct gsm_settings *set;
-
- /* Initial testcard settings */
- uint16_t mcc = 0x001, mnc = 0x01f, lac = 0x0000;
- uint32_t tmsi = 0xffffffff;
+ struct gsm322_plmn *plmn322;
+ struct msgb *nmsg;
+ struct gsm322_msg *ngm;
+ struct gsm322_plmn_list *temp;
+ struct osmo_plmn_id plmn;
+ int found = 0;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
+ plmn322 = &ms->plmn;
- if (ms->subscr.sim_valid) {
- vty_out(vty, "SIM already attached, remove first!%s",
+ if (ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
+ vty_out(vty, "Not in manual network selection mode%s",
VTY_NEWLINE);
return CMD_WARNING;
}
- set = &ms->settings;
- if (set->test_rplmn_valid) {
- mcc = set->test_rplmn_mcc;
- mnc = set->test_rplmn_mnc;
-
- if (set->test_lac > 0x0000 && set->test_lac < 0xfffe)
- lac = set->test_lac;
-
- if (set->test_tmsi != 0xffffffff)
- tmsi = set->test_tmsi;
+ if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
}
-
- if (argc == 2) {
- vty_out(vty, "Give MNC together with MCC%s", VTY_NEWLINE);
+ if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (argc >= 3) {
- mcc = gsm_input_mcc((char *)argv[1]);
- mnc = gsm_input_mnc((char *)argv[2]);
- if (mcc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (mnc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+
+ if (argc < 4) {
+ llist_for_each_entry(temp, &plmn322->sorted_plmn, entry)
+ if (osmo_plmn_cmp(&temp->plmn, &plmn) == 0)
+ found = 1;
+ if (!found) {
+ vty_out(vty, "Network not in list!%s", VTY_NEWLINE);
+ vty_out(vty, "To force selecting this network, use "
+ "'force' keyword%s", VTY_NEWLINE);
return CMD_WARNING;
}
}
- if (argc >= 4)
- lac = strtoul(argv[3], NULL, 16);
-
- if (argc >= 5)
- tmsi = strtoul(argv[4], NULL, 16);
-
- gsm_subscr_testcard(ms, mcc, mnc, lac, tmsi, attached);
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOOSE_PLMN);
+ if (!nmsg)
+ return CMD_WARNING;
+ ngm = (struct gsm322_msg *) nmsg->data;
+ memcpy(&ngm->plmn, &plmn, sizeof(struct osmo_plmn_id));
+ gsm322_plmn_sendmsg(ms, nmsg);
return CMD_SUCCESS;
}
-DEFUN(sim_test, sim_test_cmd,
- "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]",
- "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n"
- "Optionally set mobile Country Code of RPLMN\n"
- "Optionally set mobile Network Code of RPLMN\n"
- "Optionally set location area code of RPLMN\n"
- "Optionally set current assigned TMSI")
-{
- return _sim_test_cmd(vty, argc, argv, 0);
-}
+#define CALL_CMD "call MS_NAME"
+#define CALL_CMD_DESC \
+ "Call related commands\n" \
+ "Name of MS (see \"show ms\")\n"
-DEFUN(sim_test_att, sim_test_att_cmd,
- "sim testcard MS_NAME MCC MNC LAC TMSI attached",
- "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n"
- "Set mobile Country Code of RPLMN\nSet mobile Network Code of RPLMN\n"
- "Set location area code\nSet current assigned TMSI\n"
- "Indicate to MM that card is already attached")
-{
- return _sim_test_cmd(vty, argc, argv, 1);
-}
-
-DEFUN(sim_sap, sim_sap_cmd, "sim sap MS_NAME",
- "SIM actions\nAttach SIM over SAP interface\n"
- "Name of MS (see \"show ms\")\n")
+DEFUN(call_num, call_num_cmd,
+ CALL_CMD " NUMBER [(voice|data|fax)]",
+ CALL_CMD_DESC
+ "Phone number to call "
+ "(Use digits '0123456789*#abc', and '+' to dial international)\n"
+ "Initiate a regular voice call (default)\n"
+ "Initiate a data call (UDI or 3.1 kHz audio)\n"
+ "Initiate a data call (Facsimile group 3)\n")
{
struct osmocom_ms *ms;
+ struct gsm_settings *set;
+ struct gsm_settings_abbrev *abbrev;
+ enum gsm_call_type call_type;
+ const char *number;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
+ set = &ms->settings;
- if (ms->subscr.sim_valid) {
- vty_out(vty, "SIM already attached, remove first!%s",
- VTY_NEWLINE);
+ if (set->ch_cap == GSM_CAP_SDCCH) {
+ vty_out(vty, "Basic call is not supported for SDCCH only "
+ "mobile%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (gsm_subscr_sapcard(ms) != 0) {
- return CMD_WARNING;
+ number = argv[1];
+ llist_for_each_entry(abbrev, &set->abbrev, list) {
+ if (!strcmp(number, abbrev->abbrev)) {
+ number = abbrev->number;
+ vty_out(vty, "Dialing number '%s'%s", number,
+ VTY_NEWLINE);
+ break;
+ }
}
- return CMD_SUCCESS;
-}
-
-DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME",
- "SIM actions\nAttach SIM from reader\nName of MS (see \"show ms\")")
-{
- struct osmocom_ms *ms;
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ if (vty_check_number(vty, number))
return CMD_WARNING;
- if (ms->subscr.sim_valid) {
- vty_out(vty, "SIM already attached, remove first!%s",
- VTY_NEWLINE);
+ if (argc < 3 || !strcmp(argv[2], "voice"))
+ call_type = GSM_CALL_T_VOICE; /* implicit default */
+ else if (!strcmp(argv[2], "data"))
+ call_type = GSM_CALL_T_DATA;
+ else if (!strcmp(argv[2], "fax"))
+ call_type = GSM_CALL_T_DATA_FAX;
+ else
return CMD_WARNING;
- }
- gsm_subscr_simcard(ms);
+ mncc_call(ms, number, call_type);
return CMD_SUCCESS;
}
-DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME",
- "SIM actions\nDetach SIM card\nName of MS (see \"show ms\")")
+DEFUN(call, call_cmd,
+ CALL_CMD " (emergency|answer|hangup|hold)",
+ CALL_CMD_DESC
+ "Make an emergency call\n"
+ "Answer an incoming call\n"
+ "Hangup a call\n"
+ "Hold current active call\n")
{
struct osmocom_ms *ms;
+ struct gsm_settings *set;
+ const char *number;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
+ set = &ms->settings;
- if (!ms->subscr.sim_valid) {
- vty_out(vty, "No SIM attached!%s", VTY_NEWLINE);
+ if (set->ch_cap == GSM_CAP_SDCCH) {
+ vty_out(vty, "Basic call is not supported for SDCCH only "
+ "mobile%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (ms->subscr.sim_type == GSM_SIM_TYPE_SAP) {
- gsm_subscr_remove_sapcard(ms);
- }
+ number = argv[1];
+ if (!strcmp(number, "emergency"))
+ mncc_call(ms, number, GSM_CALL_T_VOICE);
+ else if (!strcmp(number, "answer"))
+ mncc_answer(ms);
+ else if (!strcmp(number, "hangup"))
+ mncc_hangup(ms);
+ else if (!strcmp(number, "hold"))
+ mncc_hold(ms);
+ else /* shall not happen */
+ OSMO_ASSERT(0);
- gsm_subscr_remove(ms);
return CMD_SUCCESS;
}
-DEFUN(sim_pin, sim_pin_cmd, "sim pin MS_NAME PIN",
- "SIM actions\nEnter PIN for SIM card\nName of MS (see \"show ms\")\n"
- "PIN number")
+DEFUN(call_retr, call_retr_cmd,
+ CALL_CMD " retrieve [NUMBER]",
+ CALL_CMD_DESC
+ "Retrieve call on hold\n"
+ "Number of call to retrieve\n")
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
- vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!ms->subscr.sim_pin_required) {
- vty_out(vty, "No PIN is required at this time!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- gsm_subscr_sim_pin(ms, (char *)argv[1], "", 0);
+ mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0);
return CMD_SUCCESS;
}
-DEFUN(sim_disable_pin, sim_disable_pin_cmd, "sim disable-pin MS_NAME PIN",
- "SIM actions\nDisable PIN of SIM card\nName of MS (see \"show ms\")\n"
- "PIN number")
+DEFUN(call_dtmf, call_dtmf_cmd,
+ CALL_CMD " dtmf DIGITS",
+ CALL_CMD_DESC
+ "Send DTMF (Dual-Tone Multi-Frequency) tones\n"
+ "One or more DTMF digits to transmit\n")
{
struct osmocom_ms *ms;
+ struct gsm_settings *set;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
+ set = &ms->settings;
- if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
- vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
+ if (!set->cc_dtmf) {
+ vty_out(vty, "DTMF not supported, please enable!%s",
+ VTY_NEWLINE);
return CMD_WARNING;
}
- gsm_subscr_sim_pin(ms, (char *)argv[1], "", -1);
+ mncc_dtmf(ms, (char *)argv[1]);
return CMD_SUCCESS;
}
-DEFUN(sim_enable_pin, sim_enable_pin_cmd, "sim enable-pin MS_NAME PIN",
- "SIM actions\nEnable PIN of SIM card\nName of MS (see \"show ms\")\n"
- "PIN number")
+#define CALL_PARAMS_CMD \
+ CALL_CMD " params"
+#define CALL_PARAMS_CMD_DESC \
+ CALL_CMD_DESC \
+ "Call related parameters\n"
+
+#define CALL_PARAMS_DATA_CMD \
+ CALL_PARAMS_CMD " data"
+#define CALL_PARAMS_DATA_CMD_DESC \
+ CALL_PARAMS_CMD_DESC \
+ "Parameters for data calls\n"
+
+#define CFG_TCH_DATA_CALL_PARAMS_CMD \
+ "call-params"
+#define CFG_TCH_DATA_CALL_PARAMS_CMD_DESC \
+ "Parameters for data calls\n"
+
+/* only supported rate/type ('<speed>' in AT+CBST) values are listed here */
+static const struct value_string data_type_rate_descs[] = {
+#if 0
+ /* TODO: rates below 2400 bps are not supported */
+ { DATA_CALL_TR_V21_300, "300 bps (V.21)" },
+ { DATA_CALL_TR_V22_1200, "1200 bps (V.22)" },
+ { DATA_CALL_TR_V23_1200_75, "1200/75 bps (V.23)" },
+#endif
+ { DATA_CALL_TR_V22bis_2400, "2400 bps (V.22bis)" },
+ { DATA_CALL_TR_V26ter_2400, "2400 bps (V.26ter)" },
+ { DATA_CALL_TR_V32_4800, "4800 bps (V.32)" },
+ { DATA_CALL_TR_V32_9600, "9600 bps (V.32)" },
+#if 0
+ /* TODO: V.34 is not supported, see notes in bcap_data_set[] */
+ { DATA_CALL_TR_V34_9600, "9600 bps (V.34)" },
+ /* TODO: rates below 2400 bps are not supported */
+ { DATA_CALL_TR_V110_300, "300 bps (V.110)" },
+ { DATA_CALL_TR_V110_1200, "1200 bps (V.110)" },
+#endif
+ { DATA_CALL_TR_V110_2400, "2400 bps (V.110 or X.31 flag stuffing)" },
+ { DATA_CALL_TR_V110_4800, "4800 bps (V.110 or X.31 flag stuffing)" },
+ { DATA_CALL_TR_V110_9600, "9600 bps (V.110 or X.31 flag stuffing)" },
+#if 0
+ /* TODO: 14400 bps is not supported */
+ { DATA_CALL_TR_V110_14400, "14400 bps (V.110 or X.31 flag stuffing)" },
+#endif
+ { 0, NULL }
+};
+
+static void _data_type_rate_cmd_string(void *ctx, struct cmd_element *cmd)
{
- struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
- if (!ms)
- return CMD_WARNING;
+ const struct value_string *vs;
+ char *string;
- if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
- vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ string = talloc_asprintf(ctx, "%s type-rate (", cmd->string);
+ for (vs = &data_type_rate_descs[0]; vs->value || vs->str; vs++)
+ string = talloc_asprintf_append(string, "%u|", vs->value);
+ string[strlen(string) - 1] = ')';
+ cmd->string = string;
+}
- gsm_subscr_sim_pin(ms, (char *)argv[1], "", 1);
+DEFUN(cfg_ms_tch_data_cp_type_rate,
+ cfg_ms_tch_data_cp_type_rate_cmd,
+ CFG_TCH_DATA_CALL_PARAMS_CMD /* generated */,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC /* generated */)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct data_call_params *cp = &ms->settings.call_params.data;
+ int val;
+
+ val = atoi(argv[0]);
+ OSMO_ASSERT(get_value_string_or_null(data_type_rate_descs, val) != NULL);
+ cp->type_rate = (enum data_call_type_rate)val;
return CMD_SUCCESS;
}
-DEFUN(sim_change_pin, sim_change_pin_cmd, "sim change-pin MS_NAME OLD NEW",
- "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n"
- "Old PIN number\nNew PIN number")
+DEFUN(call_params_data_type_rate,
+ call_params_data_type_rate_cmd,
+ CALL_PARAMS_DATA_CMD /* generated */,
+ CALL_PARAMS_DATA_CMD_DESC /* generated */)
{
- struct osmocom_ms *ms;
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ vty->index = l23_vty_get_ms(argv[0], vty);
+ if (vty->index == NULL)
return CMD_WARNING;
- if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) {
- vty_out(vty, "Old PIN must be in range 4..8!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) {
- vty_out(vty, "New PIN must be in range 4..8!%s", VTY_NEWLINE);
+ return cfg_ms_tch_data_cp_type_rate(self, vty, argc - 1, argv + 1);
+}
+
+#define CALL_PARAMS_CE_CMD \
+ "ce (transparent|non-transparent) [prefer]"
+#define CALL_PARAMS_CE_CMD_DESC \
+ "Connection element (does not apply to FAX calls)\n" \
+ "Transparent connection\n" \
+ "Non-transparent connection (RLP)\n" \
+ "Prefer the selected mode, but also accept other(s)\n"
+
+DEFUN(cfg_ms_tch_data_cp_ce,
+ cfg_ms_tch_data_cp_ce_cmd,
+ CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_CE_CMD,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_CE_CMD_DESC)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct data_call_params *cp = &ms->settings.call_params.data;
+
+ if (!strcmp(argv[0], "transparent")) {
+ if (argc > 1)
+ cp->transp = GSM48_BCAP_TR_TR_PREF;
+ else
+ cp->transp = GSM48_BCAP_TR_TRANSP;
+ } else if (!strcmp(argv[0], "non-transparent")) {
+ if (argc > 1)
+ cp->transp = GSM48_BCAP_TR_RLP_PREF;
+ else
+ cp->transp = GSM48_BCAP_TR_RLP;
+ } else { /* should not happen */
return CMD_WARNING;
}
- gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 2);
-
return CMD_SUCCESS;
}
-DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW",
- "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n"
- "Personal Unblock Key\nNew PIN number")
+DEFUN(call_params_data_ce,
+ call_params_data_ce_cmd,
+ CALL_PARAMS_DATA_CMD " " CALL_PARAMS_CE_CMD,
+ CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_CE_CMD_DESC)
{
- struct osmocom_ms *ms;
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ vty->index = l23_vty_get_ms(argv[0], vty);
+ if (vty->index == NULL)
return CMD_WARNING;
- if (strlen(argv[1]) != 8) {
- vty_out(vty, "PUC must be 8 digits!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) {
- vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return cfg_ms_tch_data_cp_ce(self, vty, argc - 1, argv + 1);
+}
+
+#define CALL_PARAMS_SYNC_ASYNC_CMD "(sync|async)"
+#define CALL_PARAMS_SYNC_ASYNC_CMD_DESC \
+ "Synchronous connection (always used for FAX calls)\n" \
+ "Asynchronous connection (does not apply to FAX calls)\n"
+
+DEFUN(cfg_ms_tch_data_cp_sync_async,
+ cfg_ms_tch_data_cp_sync_async_cmd,
+ CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_SYNC_ASYNC_CMD,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_SYNC_ASYNC_CMD_DESC)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct data_call_params *cp = &ms->settings.call_params.data;
- gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99);
+ cp->is_async = (argv[0][0] == 'a');
return CMD_SUCCESS;
}
-DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC",
- "SIM actions\nChange LAI of SIM card\nName of MS (see \"show ms\")\n"
- "Mobile Country Code\nMobile Network Code\nLocation Area Code "
- " (use 0000 to remove LAI)")
+DEFUN(call_params_data_sync_async,
+ call_params_data_sync_async_cmd,
+ CALL_PARAMS_DATA_CMD " " CALL_PARAMS_SYNC_ASYNC_CMD,
+ CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_SYNC_ASYNC_CMD_DESC)
{
- struct osmocom_ms *ms;
- uint16_t mcc = gsm_input_mcc((char *)argv[1]),
- mnc = gsm_input_mnc((char *)argv[2]),
- lac = strtoul(argv[3], NULL, 16);
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ vty->index = l23_vty_get_ms(argv[0], vty);
+ if (vty->index == NULL)
return CMD_WARNING;
- if (mcc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (mnc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return cfg_ms_tch_data_cp_sync_async(self, vty, argc - 1, argv + 1);
+}
- ms->subscr.mcc = mcc;
- ms->subscr.mnc = mnc;
- ms->subscr.lac = lac;
- ms->subscr.tmsi = 0xffffffff;
+#define CALL_PARAMS_ASYNC_CMD "async"
+#define CALL_PARAMS_ASYNC_CMD_DESC \
+ "Asynchronous connection params (does not apply to FAX calls)\n"
- gsm_subscr_write_loci(ms);
+#define CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD \
+ CALL_PARAMS_ASYNC_CMD " nr-stop-bits <1-2>"
+#define CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD_DESC \
+ CALL_PARAMS_ASYNC_CMD_DESC \
+ "Number of stop bits (soft-UART config)\n" \
+ "Number of stop bits (default: 1)\n"
+
+DEFUN(cfg_ms_tch_data_cp_async_nr_stop_bits,
+ cfg_ms_tch_data_cp_async_nr_stop_bits_cmd,
+ CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD_DESC)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct data_call_params *cp = &ms->settings.call_params.data;
+
+ cp->nr_stop_bits = atoi(argv[0]);
return CMD_SUCCESS;
}
-DEFUN(network_select, network_select_cmd,
- "network select MS_NAME MCC MNC [force]",
- "Select ...\nSelect Network\nName of MS (see \"show ms\")\n"
- "Mobile Country Code\nMobile Network Code\n"
- "Force selecting a network that is not in the list")
+DEFUN(call_params_data_async_nr_stop_bits,
+ call_params_data_async_nr_stop_bits_cmd,
+ CALL_PARAMS_DATA_CMD " " CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD,
+ CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD_DESC)
{
- struct osmocom_ms *ms;
- struct gsm322_plmn *plmn;
- struct msgb *nmsg;
- struct gsm322_msg *ngm;
- struct gsm322_plmn_list *temp;
- uint16_t mcc = gsm_input_mcc((char *)argv[1]),
- mnc = gsm_input_mnc((char *)argv[2]);
- int found = 0;
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ vty->index = l23_vty_get_ms(argv[0], vty);
+ if (vty->index == NULL)
return CMD_WARNING;
- plmn = &ms->plmn;
- if (ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
- vty_out(vty, "Not in manual network selection mode%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return cfg_ms_tch_data_cp_async_nr_stop_bits(self, vty, argc - 1, argv + 1);
+}
- if (mcc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (mnc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+#define CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD \
+ CALL_PARAMS_ASYNC_CMD " nr-data-bits <7-8>"
+#define CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD_DESC \
+ CALL_PARAMS_ASYNC_CMD_DESC \
+ "Number of data bits (soft-UART config)\n" \
+ "Number of data bits (default: 8)\n"
- if (argc < 4) {
- llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
- if (temp->mcc == mcc && temp->mnc == mnc)
- found = 1;
- if (!found) {
- vty_out(vty, "Network not in list!%s", VTY_NEWLINE);
- vty_out(vty, "To force selecting this network, use "
- "'force' keyword%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
+DEFUN(cfg_ms_tch_data_cp_async_nr_data_bits,
+ cfg_ms_tch_data_cp_async_nr_data_bits_cmd,
+ CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD_DESC)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct data_call_params *cp = &ms->settings.call_params.data;
- nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOOSE_PLMN);
- if (!nmsg)
- return CMD_WARNING;
- ngm = (struct gsm322_msg *) nmsg->data;
- ngm->mcc = mcc;
- ngm->mnc = mnc;
- gsm322_plmn_sendmsg(ms, nmsg);
+ cp->nr_data_bits = atoi(argv[0]);
return CMD_SUCCESS;
}
-DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)",
- "Make a call\nName of MS (see \"show ms\")\nPhone number to call "
- "(Use digits '0123456789*#abc', and '+' to dial international)\n"
- "Make an emergency call\nAnswer an incoming call\nHangup a call\n"
- "Hold current active call\n")
+DEFUN(call_params_data_async_nr_data_bits,
+ call_params_data_async_nr_data_bits_cmd,
+ CALL_PARAMS_DATA_CMD " " CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD,
+ CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD_DESC)
{
- struct osmocom_ms *ms;
- struct gsm_settings *set;
- struct gsm_settings_abbrev *abbrev;
- char *number;
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ vty->index = l23_vty_get_ms(argv[0], vty);
+ if (vty->index == NULL)
return CMD_WARNING;
- set = &ms->settings;
- if (set->ch_cap == GSM_CAP_SDCCH) {
- vty_out(vty, "Basic call is not supported for SDCCH only "
- "mobile%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ return cfg_ms_tch_data_cp_async_nr_data_bits(self, vty, argc - 1, argv + 1);
+}
- number = (char *)argv[1];
- if (!strcmp(number, "emergency"))
- mncc_call(ms, number);
- else if (!strcmp(number, "answer"))
- mncc_answer(ms);
- else if (!strcmp(number, "hangup"))
- mncc_hangup(ms);
- else if (!strcmp(number, "hold"))
- mncc_hold(ms);
- else {
- llist_for_each_entry(abbrev, &set->abbrev, list) {
- if (!strcmp(number, abbrev->abbrev)) {
- number = abbrev->number;
- vty_out(vty, "Dialing number '%s'%s", number,
- VTY_NEWLINE);
- break;
- }
- }
- if (vty_check_number(vty, number))
- return CMD_WARNING;
- mncc_call(ms, number);
- }
+static const struct value_string async_parity_names[] = {
+ { GSM48_BCAP_PAR_NONE, "none" },
+ { GSM48_BCAP_PAR_EVEN, "even" },
+ { GSM48_BCAP_PAR_ODD, "odd" },
+ { GSM48_BCAP_PAR_ONE, "mark" },
+ { GSM48_BCAP_PAR_ZERO, "space" },
+ { 0, NULL }
+};
- return CMD_SUCCESS;
-}
+static const struct value_string async_parity_descs[] = {
+ { GSM48_BCAP_PAR_NONE, "No parity bit (default)" },
+ { GSM48_BCAP_PAR_EVEN, "Even parity" },
+ { GSM48_BCAP_PAR_ODD, "Odd parity" },
+ { GSM48_BCAP_PAR_ONE, "Always 1" },
+ { GSM48_BCAP_PAR_ZERO, "Always 0" },
+ { 0, NULL }
+};
-DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [NUMBER]",
- "Make a call\nName of MS (see \"show ms\")\n"
- "Retrieve call on hold\nNumber of call to retrieve")
-{
- struct osmocom_ms *ms;
+#define CALL_PARAMS_ASYNC_PARITY_CMD \
+ CALL_PARAMS_ASYNC_CMD " parity"
+#define CALL_PARAMS_ASYNC_PARITY_CMD_DESC \
+ CALL_PARAMS_ASYNC_CMD_DESC \
+ "Parity mode (soft-UART config)\n"
- ms = get_ms(argv[0], vty);
- if (!ms)
- return CMD_WARNING;
+DEFUN(cfg_ms_tch_data_cp_async_parity,
+ cfg_ms_tch_data_cp_async_parity_cmd,
+ CFG_TCH_DATA_CALL_PARAMS_CMD /* generated */,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC
+ CALL_PARAMS_ASYNC_PARITY_CMD_DESC /* generated */)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct data_call_params *cp = &ms->settings.call_params.data;
+ int val;
- mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0);
+ val = get_string_value(async_parity_names, argv[0]);
+ OSMO_ASSERT(val >= 0); /* should not happen */
+ cp->parity = (enum gsm48_bcap_parity)val;
return CMD_SUCCESS;
}
-DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS",
- "Make a call\nName of MS (see \"show ms\")\n"
- "One or more DTMF digits to transmit")
+DEFUN(call_params_data_async_parity,
+ call_params_data_async_parity_cmd,
+ CALL_PARAMS_DATA_CMD /* generated */,
+ CALL_PARAMS_DATA_CMD_DESC
+ CALL_PARAMS_ASYNC_PARITY_CMD_DESC /* generated */)
{
- struct osmocom_ms *ms;
- struct gsm_settings *set;
-
- ms = get_ms(argv[0], vty);
- if (!ms)
+ vty->index = l23_vty_get_ms(argv[0], vty);
+ if (vty->index == NULL)
return CMD_WARNING;
- set = &ms->settings;
-
- if (!set->cc_dtmf) {
- vty_out(vty, "DTMF not supported, please enable!%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- mncc_dtmf(ms, (char *)argv[1]);
-
- return CMD_SUCCESS;
+ return cfg_ms_tch_data_cp_async_parity(self, vty, argc - 1, argv + 1);
}
DEFUN(sms, sms_cmd, "sms MS_NAME NUMBER .LINE",
@@ -913,7 +926,7 @@ DEFUN(sms, sms_cmd, "sms MS_NAME NUMBER .LINE",
struct gsm_settings_abbrev *abbrev;
char *number, *sms_sca = NULL;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
set = &ms->settings;
@@ -972,7 +985,7 @@ DEFUN(service, service_cmd, "service MS_NAME (*#06#|*#21#|*#67#|*#61#|*#62#"
{
struct osmocom_ms *ms;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
@@ -981,14 +994,157 @@ DEFUN(service, service_cmd, "service MS_NAME (*#06#|*#21#|*#67#|*#61#|*#62#"
return CMD_SUCCESS;
}
+#define VGCS_STR "Make a voice group call\nName of MS (see \"show ms\")\n"
+#define VGCS_CMDS "(CALLREF|hangup|leave|talk|listen)"
+#define VGCS_CMDS_TXT \
+ "Voice group to call or join\nHangup voice group call\nLeave voice group call\nBecome talker\nBecome listener"
+
+/* This command enters VGCS call node with given MS. */
+DEFUN(vgcs_enter, vgcs_enter_cmd, "group-call MS_NAME",
+ VGCS_STR)
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ vty->index = ms;
+ vty->node = VGCS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+/* These commands perform VGCS on VGCS node. */
+DEFUN(vgcs, vgcs_cmd, VGCS_CMDS,
+ VGCS_CMDS_TXT)
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set;
+ const char *command;
+
+ set = &ms->settings;
+
+ if (!set->vgcs) {
+ vty_out(vty, "VGCS not supported by this mobile, please enable VGCS support%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (set->ch_cap == GSM_CAP_SDCCH) {
+ vty_out(vty, "ASCI call is not supported for SDCCH only mobile%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ command = (char *)argv[0];
+ if (!strcmp(command, "hangup"))
+ gcc_bcc_hangup(ms);
+ else if (!strcmp(command, "leave"))
+ gcc_leave(ms);
+ else if (!strcmp(command, "talk"))
+ gcc_talk(ms);
+ else if (!strcmp(command, "listen"))
+ gcc_listen(ms);
+ else {
+ if (vty_check_callref(vty, command))
+ return CMD_WARNING;
+ gcc_bcc_call(ms, GSM48_PDISC_GROUP_CC, command);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* These commands perform VGCS on given MS without entering the VGCS node. */
+DEFUN(vgcs_direct, vgcs_direct_cmd, "group-call MS_NAME " VGCS_CMDS,
+ VGCS_STR VGCS_CMDS_TXT)
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ vty->index = ms;
+ return vgcs(self, vty, argc - 1, argv + 1);
+}
+
+#define VBS_STR "Make a voice broadcast call\nName of MS (see \"show ms\")\n"
+#define VBS_CMDS "(CALLREF|hangup)"
+#define VBS_CMDS_TXT \
+ "Voice broadcast to call or join\nHangup voice broadcast call"
+
+/* This command enters VBS call node with given MS. */
+DEFUN(vbs_enter, vbs_enter_cmd, "broadcast-call MS_NAME",
+ VBS_STR)
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ vty->index = ms;
+ vty->node = VBS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+/* These commands perform VBS on VBS node. */
+DEFUN(vbs, vbs_cmd, VBS_CMDS,
+ VBS_CMDS_TXT)
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set;
+ const char *command;
+
+ set = &ms->settings;
+
+ if (!set->vbs) {
+ vty_out(vty, "VBS not supported by this mobile, please enable VBS support%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (set->ch_cap == GSM_CAP_SDCCH) {
+ vty_out(vty, "ASCI call is not supported for SDCCH only mobile%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ command = (char *)argv[0];
+ if (!strcmp(command, "hangup"))
+ gcc_bcc_hangup(ms);
+ else {
+ if (vty_check_callref(vty, command))
+ return CMD_WARNING;
+ gcc_bcc_call(ms, GSM48_PDISC_BCAST_CC, command);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* These commands perform VBS on given MS without entering the VBS node. */
+DEFUN(vbs_direct, vbs_direct_cmd, "broadcast-call MS_NAME " VBS_CMDS,
+ VBS_STR VBS_CMDS_TXT)
+{
+ struct osmocom_ms *ms;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ vty->index = ms;
+ return vbs(self, vty, argc - 1, argv + 1);
+}
+
+#define TEST_CMD_DESC "Special commands for testing\n"
+
DEFUN(test_reselection, test_reselection_cmd, "test re-selection NAME",
- "Manually trigger cell re-selection\nName of MS (see \"show ms\")")
+ TEST_CMD_DESC "Manually trigger cell re-selection\n"
+ "Name of MS (see \"show ms\")")
{
struct osmocom_ms *ms;
struct gsm_settings *set;
struct msgb *nmsg;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
set = &ms->settings;
@@ -1014,23 +1170,22 @@ DEFUN(delete_forbidden_plmn, delete_forbidden_plmn_cmd,
"Mobile Country Code\nMobile Network Code")
{
struct osmocom_ms *ms;
- uint16_t mcc = gsm_input_mcc((char *)argv[1]),
- mnc = gsm_input_mnc((char *)argv[2]);
+ struct osmo_plmn_id plmn;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- if (mcc == GSM_INPUT_INVALID) {
+ if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) {
vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
return CMD_WARNING;
}
- if (mnc == GSM_INPUT_INVALID) {
+ if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) {
vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
return CMD_WARNING;
}
- gsm_subscr_del_forbidden_plmn(&ms->subscr, mcc, mnc);
+ gsm_subscr_del_forbidden_plmn(&ms->subscr, &plmn);
return CMD_SUCCESS;
}
@@ -1044,7 +1199,7 @@ DEFUN(network_show, network_show_cmd, "network show MS_NAME",
struct gsm322_plmn *plmn;
struct gsm322_plmn_list *temp;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
set = &ms->settings;
@@ -1058,9 +1213,10 @@ DEFUN(network_show, network_show_cmd, "network show MS_NAME",
llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
vty_out(vty, " Network %s, %s (%s, %s)%s",
- gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
- gsm_get_mcc(temp->mcc),
- gsm_get_mnc(temp->mcc, temp->mnc), VTY_NEWLINE);
+ osmo_mcc_name(temp->plmn.mcc),
+ osmo_mnc_name(temp->plmn.mnc, temp->plmn.mnc_3_digits),
+ gsm_get_mcc(temp->plmn.mcc),
+ gsm_get_mnc(&temp->plmn), VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1071,7 +1227,7 @@ DEFUN(network_search, network_search_cmd, "network search MS_NAME",
struct osmocom_ms *ms;
struct msgb *nmsg;
- ms = get_ms(argv[0], vty);
+ ms = l23_vty_get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
@@ -1177,22 +1333,6 @@ DEFUN(cfg_gps_baud, cfg_gps_baud_cmd, "gps baudrate "
return CMD_SUCCESS;
}
-DEFUN(cfg_hide_default, cfg_hide_default_cmd, "hide-default",
- "Hide most default values in config to make it more compact")
-{
- hide_default = 1;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_no_hide_default, cfg_no_hide_default_cmd, "no hide-default",
- NO_STR "Show default values in config")
-{
- hide_default = 0;
-
- return CMD_SUCCESS;
-}
-
/* per MS config */
DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME",
"Select a mobile station to configure\nName of MS (see \"show ms\")")
@@ -1208,7 +1348,7 @@ DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME",
}
if (!found) {
- if (!vty_reading) {
+ if (!l23_vty_reading) {
vty_out(vty, "MS name '%s' does not exits, try "
"'ms %s create'%s", argv[0], argv[0],
VTY_NEWLINE);
@@ -1311,7 +1451,7 @@ DEFUN(cfg_no_ms, cfg_no_ms_cmd, "no ms MS_NAME",
#define SUP_WRITE(item, cmd) \
if (sup->item) \
- if (!hide_default || !set->item) \
+ if (!l23_vty_hide_default || !set->item) \
vty_out(vty, " %s%s%s", (set->item) ? "" : "no ", \
cmd, VTY_NEWLINE);
@@ -1322,58 +1462,48 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
struct gsm_settings_abbrev *abbrev;
vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE);
- vty_out(vty, " layer2-socket %s%s", set->layer2_socket_path,
- VTY_NEWLINE);
+
+ l23_vty_config_write_ms_node_contents(vty, ms, " ");
+
vty_out(vty, " sap-socket %s%s", set->sap_socket_path, VTY_NEWLINE);
- switch(set->sim_type) {
- case GSM_SIM_TYPE_NONE:
- vty_out(vty, " sim none%s", VTY_NEWLINE);
- break;
- case GSM_SIM_TYPE_L1PHY:
- vty_out(vty, " sim reader%s", VTY_NEWLINE);
- break;
- case GSM_SIM_TYPE_TEST:
- vty_out(vty, " sim test%s", VTY_NEWLINE);
+ vty_out(vty, " mncc-socket %s%s", set->mncc_socket_path, VTY_NEWLINE);
+ switch (set->mncc_handler) {
+ case MNCC_HANDLER_INTERNAL:
+ vty_out(vty, " mncc-handler internal%s", VTY_NEWLINE);
break;
- case GSM_SIM_TYPE_SAP:
- vty_out(vty, " sim sap%s", VTY_NEWLINE);
+ case MNCC_HANDLER_EXTERNAL:
+ vty_out(vty, " mncc-handler external%s", VTY_NEWLINE);
break;
+ case MNCC_HANDLER_DUMMY:
+ vty_out(vty, " mncc-handler dummy%s", VTY_NEWLINE);
}
vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode
== PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE);
- vty_out(vty, " imei %s %s%s", set->imei,
- set->imeisv + strlen(set->imei), VTY_NEWLINE);
- if (set->imei_random)
- vty_out(vty, " imei-random %d%s", set->imei_random,
- VTY_NEWLINE);
- else
- if (!hide_default)
- vty_out(vty, " imei-fixed%s", VTY_NEWLINE);
if (set->emergency_imsi[0])
vty_out(vty, " emergency-imsi %s%s", set->emergency_imsi,
VTY_NEWLINE);
else
- if (!hide_default)
+ if (!l23_vty_hide_default)
vty_out(vty, " no emergency-imsi%s", VTY_NEWLINE);
if (set->sms_sca[0])
vty_out(vty, " sms-service-center %s%s", set->sms_sca,
VTY_NEWLINE);
else
- if (!hide_default)
+ if (!l23_vty_hide_default)
vty_out(vty, " no sms-service-center%s", VTY_NEWLINE);
- if (!hide_default || set->cw)
+ if (!l23_vty_hide_default || set->cw)
vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ",
VTY_NEWLINE);
- if (!hide_default || set->auto_answer)
+ if (!l23_vty_hide_default || set->auto_answer)
vty_out(vty, " %sauto-answer%s",
(set->auto_answer) ? "" : "no ", VTY_NEWLINE);
- if (!hide_default || set->force_rekey)
+ if (!l23_vty_hide_default || set->force_rekey)
vty_out(vty, " %sforce-rekey%s",
(set->force_rekey) ? "" : "no ", VTY_NEWLINE);
- if (!hide_default || set->clip)
+ if (!l23_vty_hide_default || set->clip)
vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ",
VTY_NEWLINE);
- if (!hide_default || set->clir)
+ if (!l23_vty_hide_default || set->clir)
vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ",
VTY_NEWLINE);
if (set->alter_tx_power)
@@ -1383,25 +1513,25 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
else
vty_out(vty, " tx-power full%s", VTY_NEWLINE);
else
- if (!hide_default)
+ if (!l23_vty_hide_default)
vty_out(vty, " tx-power auto%s", VTY_NEWLINE);
if (set->alter_delay)
vty_out(vty, " simulated-delay %d%s", set->alter_delay,
VTY_NEWLINE);
else
- if (!hide_default)
+ if (!l23_vty_hide_default)
vty_out(vty, " no simulated-delay%s", VTY_NEWLINE);
if (set->stick)
vty_out(vty, " stick %d%s%s", set->stick_arfcn & 1023,
(set->stick_arfcn & ARFCN_PCS) ? " pcs" : "",
VTY_NEWLINE);
else
- if (!hide_default)
+ if (!l23_vty_hide_default)
vty_out(vty, " no stick%s", VTY_NEWLINE);
- if (!hide_default || set->no_lupd)
+ if (!l23_vty_hide_default || set->no_lupd)
vty_out(vty, " %slocation-updating%s",
(set->no_lupd) ? "no " : "", VTY_NEWLINE);
- if (!hide_default || set->no_neighbour)
+ if (!l23_vty_hide_default || set->no_neighbour)
vty_out(vty, " %sneighbour-measurement%s",
(set->no_neighbour) ? "no " : "", VTY_NEWLINE);
if (set->full_v1 || set->full_v2 || set->full_v3) {
@@ -1419,7 +1549,7 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
vty_out(vty, " no codec half-speed%s", VTY_NEWLINE);
}
if (llist_empty(&set->abbrev)) {
- if (!hide_default)
+ if (!l23_vty_hide_default)
vty_out(vty, " no abbrev%s", VTY_NEWLINE);
} else {
llist_for_each_entry(abbrev, &set->abbrev, list)
@@ -1439,32 +1569,32 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
SUP_WRITE(p_gsm, "p-gsm");
SUP_WRITE(e_gsm, "e-gsm");
SUP_WRITE(r_gsm, "r-gsm");
- SUP_WRITE(pcs, "gsm-850");
+ SUP_WRITE(gsm_850, "gsm-850");
SUP_WRITE(gsm_480, "gsm-480");
SUP_WRITE(gsm_450, "gsm-450");
SUP_WRITE(dcs, "dcs");
SUP_WRITE(pcs, "pcs");
if (sup->r_gsm || sup->e_gsm || sup->p_gsm)
- if (!hide_default || sup->class_900 != set->class_900)
+ if (!l23_vty_hide_default || sup->class_900 != set->class_900)
vty_out(vty, " class-900 %d%s", set->class_900,
VTY_NEWLINE);
if (sup->gsm_850)
- if (!hide_default || sup->class_850 != set->class_850)
+ if (!l23_vty_hide_default || sup->class_850 != set->class_850)
vty_out(vty, " class-850 %d%s", set->class_850,
VTY_NEWLINE);
if (sup->gsm_480 || sup->gsm_450)
- if (!hide_default || sup->class_400 != set->class_400)
+ if (!l23_vty_hide_default || sup->class_400 != set->class_400)
vty_out(vty, " class-400 %d%s", set->class_400,
VTY_NEWLINE);
if (sup->dcs)
- if (!hide_default || sup->class_dcs != set->class_dcs)
+ if (!l23_vty_hide_default || sup->class_dcs != set->class_dcs)
vty_out(vty, " class-dcs %d%s", set->class_dcs,
VTY_NEWLINE);
if (sup->pcs)
- if (!hide_default || sup->class_pcs != set->class_pcs)
+ if (!l23_vty_hide_default || sup->class_pcs != set->class_pcs)
vty_out(vty, " class-pcs %d%s", set->class_pcs,
VTY_NEWLINE);
- if (!hide_default || sup->ch_cap != set->ch_cap) {
+ if (!l23_vty_hide_default || sup->ch_cap != set->ch_cap) {
switch (set->ch_cap) {
case GSM_CAP_SDCCH:
vty_out(vty, " channel-capability sdcch%s",
@@ -1485,63 +1615,84 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
SUP_WRITE(full_v3, "full-speech-v3");
SUP_WRITE(half_v1, "half-speech-v1");
SUP_WRITE(half_v3, "half-speech-v3");
- if (!hide_default || sup->min_rxlev_dbm != set->min_rxlev_dbm)
+ SUP_WRITE(csd_tch_f144, "full-data-14400");
+ SUP_WRITE(csd_tch_f96, "full-data-9600");
+ SUP_WRITE(csd_tch_f48, "full-data-4800");
+ SUP_WRITE(csd_tch_h48, "half-data-4800");
+ SUP_WRITE(csd_tch_f24, "full-data-2400");
+ SUP_WRITE(csd_tch_h24, "half-data-2400");
+ if (!l23_vty_hide_default || sup->min_rxlev_dbm != set->min_rxlev_dbm)
vty_out(vty, " min-rxlev %d%s", set->min_rxlev_dbm,
VTY_NEWLINE);
- if (!hide_default || sup->dsc_max != set->dsc_max)
+ if (!l23_vty_hide_default || sup->dsc_max != set->dsc_max)
vty_out(vty, " dsc-max %d%s", set->dsc_max, VTY_NEWLINE);
- if (!hide_default || set->skip_max_per_band)
+ if (!l23_vty_hide_default || set->skip_max_per_band)
vty_out(vty, " %sskip-max-per-band%s",
(set->skip_max_per_band) ? "" : "no ", VTY_NEWLINE);
- vty_out(vty, " test-sim%s", VTY_NEWLINE);
- vty_out(vty, " imsi %s%s", set->test_imsi, VTY_NEWLINE);
- switch (set->test_ki_type) {
- case OSMO_AUTH_ALG_XOR:
- vty_out(vty, " ki xor %s%s",
- osmo_hexdump(set->test_ki, 12), VTY_NEWLINE);
+ SUP_WRITE(vgcs, "vgcs");
+ SUP_WRITE(vbs, "vbs");
+ if (!l23_vty_hide_default || set->any_timeout != MOB_C7_DEFLT_ANY_TIMEOUT)
+ vty_out(vty, " c7-any-timeout %d%s",
+ set->any_timeout, VTY_NEWLINE);
+ if (!l23_vty_hide_default || !set->uplink_release_local)
+ vty_out(vty, " %suplink-release-local%s",
+ (!set->uplink_release_local) ? "no " : "", VTY_NEWLINE);
+ if (!l23_vty_hide_default || set->asci_allow_any)
+ vty_out(vty, " %sasci-allow-any%s",
+ (set->asci_allow_any) ? "" : "no ", VTY_NEWLINE);
+
+ vty_out(vty, " tch-voice%s", VTY_NEWLINE);
+ vty_out(vty, " io-handler %s%s",
+ tch_voice_io_handler_name(set->tch_voice.io_handler), VTY_NEWLINE);
+ if (set->tch_voice.io_handler == TCH_VOICE_IOH_GAPK) {
+ vty_out(vty, " io-tch-format %s%s",
+ tch_voice_io_format_name(set->tch_voice.io_format), VTY_NEWLINE);
+ vty_out(vty, " alsa-output-dev %s%s",
+ &set->tch_voice.alsa_output_dev[0], VTY_NEWLINE);
+ vty_out(vty, " alsa-input-dev %s%s",
+ &set->tch_voice.alsa_input_dev[0], VTY_NEWLINE);
+ }
+
+ vty_out(vty, " tch-data%s", VTY_NEWLINE);
+ vty_out(vty, " io-handler %s%s",
+ tch_data_io_handler_name(set->tch_data.io_handler), VTY_NEWLINE);
+ vty_out(vty, " io-tch-format %s%s",
+ tch_data_io_format_name(set->tch_data.io_format), VTY_NEWLINE);
+ if (set->tch_data.io_handler == TCH_DATA_IOH_UNIX_SOCK) {
+ vty_out(vty, " unix-socket %s%s",
+ set->tch_data.unix_socket_path, VTY_NEWLINE);
+ }
+
+ vty_out(vty, " call-params type-rate %d%s",
+ (int)set->call_params.data.type_rate, VTY_NEWLINE);
+ switch (set->call_params.data.transp) {
+ case GSM48_BCAP_TR_TR_PREF:
+ vty_out(vty, " call-params ce transparent prefer%s", VTY_NEWLINE);
+ break;
+ case GSM48_BCAP_TR_TRANSP:
+ vty_out(vty, " call-params ce transparent%s", VTY_NEWLINE);
+ break;
+ case GSM48_BCAP_TR_RLP_PREF:
+ vty_out(vty, " call-params ce non-transparent prefer%s", VTY_NEWLINE);
break;
- case OSMO_AUTH_ALG_COMP128v1:
- vty_out(vty, " ki comp128 %s%s",
- osmo_hexdump(set->test_ki, 16), VTY_NEWLINE);
+ case GSM48_BCAP_TR_RLP:
+ vty_out(vty, " call-params ce non-transparent%s", VTY_NEWLINE);
break;
}
- if (!hide_default || set->test_barr)
- vty_out(vty, " %sbarred-access%s",
- (set->test_barr) ? "" : "no ", VTY_NEWLINE);
- if (set->test_rplmn_valid) {
- vty_out(vty, " rplmn %s %s",
- gsm_print_mcc(set->test_rplmn_mcc),
- gsm_print_mnc(set->test_rplmn_mnc));
- if (set->test_lac > 0x0000 && set->test_lac < 0xfffe) {
- vty_out(vty, " 0x%04x", set->test_lac);
- if (set->test_tmsi != 0xffffffff) {
- vty_out(vty, " 0x%08x", set->test_tmsi);
- if (set->test_imsi_attached)
- vty_out(vty, " attached");
- }
- }
- vty_out(vty, "%s", VTY_NEWLINE);
- } else
- if (!hide_default)
- vty_out(vty, " no rplmn%s", VTY_NEWLINE);
- if (!hide_default || set->test_always)
- vty_out(vty, " hplmn-search %s%s",
- (set->test_always) ? "everywhere" : "foreign-country",
- VTY_NEWLINE);
- if (!hide_default || set->any_timeout != MOB_C7_DEFLT_ANY_TIMEOUT)
- vty_out(vty, " c7-any-timeout %d%s",
- set->any_timeout, VTY_NEWLINE);
-
- vty_out(vty, " audio%s", VTY_NEWLINE);
- if (!hide_default || set->audio.io_handler != AUDIO_IOH_NONE)
- vty_out(vty, " io-handler %s%s", audio_io_handler_name(set->audio.io_handler), VTY_NEWLINE);
-
- /* no shutdown must be written to config, because shutdown is default */
- vty_out(vty, " %sshutdown%s", (ms->shutdown != MS_SHUTDOWN_NONE) ? "" : "no ",
+ vty_out(vty, " call-params %s%s",
+ set->call_params.data.is_async ? "async" : "sync", VTY_NEWLINE);
+ vty_out(vty, " call-params async nr-stop-bits %u%s",
+ set->call_params.data.nr_stop_bits, VTY_NEWLINE);
+ vty_out(vty, " call-params async nr-data-bits %u%s",
+ set->call_params.data.nr_data_bits, VTY_NEWLINE);
+ vty_out(vty, " call-params async parity %s%s",
+ get_value_string(async_parity_names, set->call_params.data.parity),
VTY_NEWLINE);
+
if (ms->lua_script)
vty_out(vty, " lua-script %s%s", ms->lua_script, VTY_NEWLINE);
- vty_out(vty, "!%s", VTY_NEWLINE);
+
+ l23_vty_config_write_ms_node_contents_final(vty, ms, " ");
}
static int config_write(struct vty *vty)
@@ -1559,7 +1710,7 @@ static int config_write(struct vty *vty)
vty_out(vty, "%sgps enable%s", (g.enable) ? "" : "no ", VTY_NEWLINE);
vty_out(vty, "!%s", VTY_NEWLINE);
- vty_out(vty, "%shide-default%s", (hide_default) ? "": "no ",
+ vty_out(vty, "%shide-default%s", (l23_vty_hide_default) ? "" : "no ",
VTY_NEWLINE);
vty_out(vty, "!%s", VTY_NEWLINE);
@@ -1579,60 +1730,75 @@ DEFUN(cfg_ms_show_this, cfg_ms_show_this_cmd, "show this",
return CMD_SUCCESS;
}
-DEFUN(cfg_ms_layer2, cfg_ms_layer2_cmd, "layer2-socket PATH",
- "Define socket path to connect between layer 2 and layer 1\n"
- "Unix socket, default '/tmp/osmocom_l2'")
+DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH",
+ "Define socket path to connect to SIM reader\n"
+ "Unix socket, default '/tmp/osmocom_sap'")
{
struct osmocom_ms *ms = vty->index;
struct gsm_settings *set = &ms->settings;
- OSMO_STRLCPY_ARRAY(set->layer2_socket_path, argv[0]);
+ OSMO_STRLCPY_ARRAY(set->sap_socket_path, argv[0]);
vty_restart(vty, ms);
return CMD_SUCCESS;
}
-DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH",
- "Define socket path to connect to SIM reader\n"
- "Unix socket, default '/tmp/osmocom_sap'")
+DEFUN(cfg_ms_mncc_sock, cfg_ms_mncc_sock_cmd, "mncc-socket PATH",
+ "Define socket path for MNCC interface\n"
+ "UNIX socket path (default '/tmp/ms_mncc_' + MS_NAME)")
{
struct osmocom_ms *ms = vty->index;
struct gsm_settings *set = &ms->settings;
- OSMO_STRLCPY_ARRAY(set->sap_socket_path, argv[0]);
+ OSMO_STRLCPY_ARRAY(set->mncc_socket_path, argv[0]);
vty_restart(vty, ms);
return CMD_SUCCESS;
}
-DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test|sap)",
- "Set SIM card to attach when powering on\nAttach no SIM\n"
- "Attach SIM from reader\nAttach build in test SIM\n"
- "Attach SIM over SAP interface")
+DEFUN(cfg_ms_mncc_handler, cfg_ms_mncc_handler_cmd,
+ "mncc-handler (internal|external|dummy)",
+ "Set MNCC (Call Control) handler\n"
+ "Built-in MNCC handler (default)\n"
+ "External MNCC application via UNIX-socket (e.g. LCR)\n"
+ "Dummy MNCC handler (no Call Control)\n")
{
struct osmocom_ms *ms = vty->index;
struct gsm_settings *set = &ms->settings;
switch (argv[0][0]) {
- case 'n':
- set->sim_type = GSM_SIM_TYPE_NONE;
- break;
- case 'r':
- set->sim_type = GSM_SIM_TYPE_L1PHY;
+ case 'i':
+ if (set->ch_cap == GSM_CAP_SDCCH) { /* SDCCH only */
+ vty_out(vty, "TCH support is disabled, "
+ "check 'channel-capability' param%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ set->mncc_handler = MNCC_HANDLER_INTERNAL;
break;
- case 't':
- set->sim_type = GSM_SIM_TYPE_TEST;
+ case 'e':
+ set->mncc_handler = MNCC_HANDLER_EXTERNAL;
break;
- case 's':
- set->sim_type = GSM_SIM_TYPE_SAP;
+ case 'd':
+ set->mncc_handler = MNCC_HANDLER_DUMMY;
break;
default:
- vty_out(vty, "unknown SIM type%s", VTY_NEWLINE);
- return CMD_WARNING;
+ /* Shall not happen */
+ OSMO_ASSERT(0);
}
vty_restart_if_started(vty, ms);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_no_mncc_handler, cfg_ms_no_mncc_handler_cmd,
+ "no mncc-handler", NO_STR "Disable Call Control")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->mncc_handler = MNCC_HANDLER_DUMMY;
+ vty_restart_if_started(vty, ms);
return CMD_SUCCESS;
}
@@ -1662,63 +1828,14 @@ DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)",
return CMD_SUCCESS;
}
-DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]",
- "Set IMEI (enter without control digit)\n15 Digits IMEI\n"
- "Software version digit")
-{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
- char *error, *sv = "0";
-
- if (argc >= 2)
- sv = (char *)argv[1];
-
- error = gsm_check_imei(argv[0], sv);
- if (error) {
- vty_out(vty, "%s%s", error, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- strcpy(set->imei, argv[0]);
- strcpy(set->imeisv, argv[0]);
- strcpy(set->imeisv + 15, sv);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed",
- "Use fixed IMEI on every power on")
-{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
-
- set->imei_random = 0;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>",
- "Use random IMEI on every power on\n"
- "Number of trailing digits to randomize")
-{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
-
- set->imei_random = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi IMSI",
"Use special IMSI for emergency calls\n15 digits IMSI")
{
struct osmocom_ms *ms = vty->index;
struct gsm_settings *set = &ms->settings;
- char *error;
- error = gsm_check_imsi(argv[0]);
- if (error) {
- vty_out(vty, "%s%s", error, VTY_NEWLINE);
+ if (!osmo_imsi_str_valid(argv[0])) {
+ vty_out(vty, "Wrong IMSI format%s", VTY_NEWLINE);
return CMD_WARNING;
}
strcpy(set->emergency_imsi, argv[0]);
@@ -2209,6 +2326,48 @@ DEFUN(cfg_ms_any_timeout, cfg_ms_any_timeout_cmd, "c7-any-timeout <0-255>",
return CMD_SUCCESS;
}
+DEFUN(cfg_ms_no_uplink_release_local, cfg_ms_no_uplink_release_local_cmd, "no uplink-release-local",
+ NO_STR "Release L2 on uplink of VGCS channel normally. Release locally when UPLINK FREE is received.")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->uplink_release_local = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_uplink_release_local, cfg_ms_uplink_release_local_cmd, "uplink-release-local",
+ "Release L2 on uplink of VGCS channel locally after receiving UPLINK FREE.")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->uplink_release_local = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_asci_allow_any, cfg_ms_asci_allow_any_cmd, "asci-allow-any",
+ "Allow any ASCI related call feature, even if service is limited or SIM invalid.")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->asci_allow_any = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_no_asci_allow_any, cfg_ms_no_asci_allow_any_cmd, "no asci-allow-any",
+ NO_STR "Do not allow any ASCI related call feature, if service is not normal.")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ set->asci_allow_any = false;
+
+ return CMD_SUCCESS;
+}
+
static int config_write_dummy(struct vty *vty)
{
return CMD_SUCCESS;
@@ -2223,15 +2382,17 @@ DEFUN(cfg_ms_support, cfg_ms_support_cmd, "support",
return CMD_SUCCESS;
}
-#define SUP_EN(cfg, cfg_cmd, item, cmd, desc, restart) \
-DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
+#define SUP_EN(item, cmd, desc, restart) \
+DEFUN(cfg_ms_sup_en_##item, \
+ cfg_ms_sup_en_##item##_cmd, \
+ cmd, "Enable " desc "support") \
{ \
struct osmocom_ms *ms = vty->index; \
struct gsm_settings *set = &ms->settings; \
struct gsm_support *sup = &ms->support; \
if (!sup->item) { \
vty_out(vty, desc " not supported%s", VTY_NEWLINE); \
- if (vty_reading) \
+ if (l23_vty_reading) \
return CMD_SUCCESS; \
return CMD_WARNING; \
} \
@@ -2241,15 +2402,17 @@ DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
return CMD_SUCCESS; \
}
-#define SUP_DI(cfg, cfg_cmd, item, cmd, desc, restart) \
-DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
+#define SUP_DI(item, cmd, desc, restart) \
+DEFUN(cfg_ms_sup_di_##item, \
+ cfg_ms_sup_di_##item##_cmd, \
+ "no " cmd, NO_STR "Disable " desc " support") \
{ \
struct osmocom_ms *ms = vty->index; \
struct gsm_settings *set = &ms->settings; \
struct gsm_support *sup = &ms->support; \
if (!sup->item) { \
vty_out(vty, desc " not supported%s", VTY_NEWLINE); \
- if (vty_reading) \
+ if (l23_vty_reading) \
return CMD_SUCCESS; \
return CMD_WARNING; \
} \
@@ -2259,8 +2422,15 @@ DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
return CMD_SUCCESS; \
}
-#define SET_EN(cfg, cfg_cmd, item, cmd, desc, restart) \
-DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
+#define SUP_EN_DI(item, cmd, desc, restart) \
+ SUP_EN(item, cmd, desc, restart); \
+ SUP_DI(item, cmd, desc, restart)
+
+
+#define SET_EN(item, cmd, desc, restart) \
+DEFUN(cfg_ms_set_en_##item, \
+ cfg_ms_set_en_##item##_cmd, \
+ cmd, "Enable " desc "support") \
{ \
struct osmocom_ms *ms = vty->index; \
struct gsm_settings *set = &ms->settings; \
@@ -2270,8 +2440,10 @@ DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
return CMD_SUCCESS; \
}
-#define SET_DI(cfg, cfg_cmd, item, cmd, desc, restart) \
-DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
+#define SET_DI(item, cmd, desc, restart) \
+DEFUN(cfg_ms_set_di_##item, \
+ cfg_ms_set_di_##item##_cmd, \
+ "no " cmd, NO_STR "Disable " desc " support") \
{ \
struct osmocom_ms *ms = vty->index; \
struct gsm_settings *set = &ms->settings; \
@@ -2281,52 +2453,28 @@ DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
return CMD_SUCCESS; \
}
-SET_EN(cfg_ms_sup_dtmf, cfg_ms_sup_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0);
-SET_DI(cfg_ms_sup_no_dtmf, cfg_ms_sup_no_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0);
-SUP_EN(cfg_ms_sup_sms, cfg_ms_sup_sms_cmd, sms_ptp, "sms", "SMS", 0);
-SUP_DI(cfg_ms_sup_no_sms, cfg_ms_sup_no_sms_cmd, sms_ptp, "sms", "SMS", 0);
-SUP_EN(cfg_ms_sup_a5_1, cfg_ms_sup_a5_1_cmd, a5_1, "a5/1", "A5/1", 0);
-SUP_DI(cfg_ms_sup_no_a5_1, cfg_ms_sup_no_a5_1_cmd, a5_1, "a5/1", "A5/1", 0);
-SUP_EN(cfg_ms_sup_a5_2, cfg_ms_sup_a5_2_cmd, a5_2, "a5/2", "A5/2", 0);
-SUP_DI(cfg_ms_sup_no_a5_2, cfg_ms_sup_no_a5_2_cmd, a5_2, "a5/2", "A5/2", 0);
-SUP_EN(cfg_ms_sup_a5_3, cfg_ms_sup_a5_3_cmd, a5_3, "a5/3", "A5/3", 0);
-SUP_DI(cfg_ms_sup_no_a5_3, cfg_ms_sup_no_a5_3_cmd, a5_3, "a5/3", "A5/3", 0);
-SUP_EN(cfg_ms_sup_a5_4, cfg_ms_sup_a5_4_cmd, a5_4, "a5/4", "A5/4", 0);
-SUP_DI(cfg_ms_sup_no_a5_4, cfg_ms_sup_no_a5_4_cmd, a5_4, "a5/4", "A5/4", 0);
-SUP_EN(cfg_ms_sup_a5_5, cfg_ms_sup_a5_5_cmd, a5_5, "a5/5", "A5/5", 0);
-SUP_DI(cfg_ms_sup_no_a5_5, cfg_ms_sup_no_a5_5_cmd, a5_5, "a5/5", "A5/5", 0);
-SUP_EN(cfg_ms_sup_a5_6, cfg_ms_sup_a5_6_cmd, a5_6, "a5/6", "A5/6", 0);
-SUP_DI(cfg_ms_sup_no_a5_6, cfg_ms_sup_no_a5_6_cmd, a5_6, "a5/6", "A5/6", 0);
-SUP_EN(cfg_ms_sup_a5_7, cfg_ms_sup_a5_7_cmd, a5_7, "a5/7", "A5/7", 0);
-SUP_DI(cfg_ms_sup_no_a5_7, cfg_ms_sup_no_a5_7_cmd, a5_7, "a5/7", "A5/7", 0);
-SUP_EN(cfg_ms_sup_p_gsm, cfg_ms_sup_p_gsm_cmd, p_gsm, "p-gsm", "P-GSM (900)",
- 1);
-SUP_DI(cfg_ms_sup_no_p_gsm, cfg_ms_sup_no_p_gsm_cmd, p_gsm, "p-gsm",
- "P-GSM (900)", 1);
-SUP_EN(cfg_ms_sup_e_gsm, cfg_ms_sup_e_gsm_cmd, e_gsm, "e-gsm", "E-GSM (850)",
- 1);
-SUP_DI(cfg_ms_sup_no_e_gsm, cfg_ms_sup_no_e_gsm_cmd, e_gsm, "e-gsm",
- "E-GSM (850)", 1);
-SUP_EN(cfg_ms_sup_r_gsm, cfg_ms_sup_r_gsm_cmd, r_gsm, "r-gsm", "R-GSM (850)",
- 1);
-SUP_DI(cfg_ms_sup_no_r_gsm, cfg_ms_sup_no_r_gsm_cmd, r_gsm, "r-gsm",
- "R-GSM (850)", 1);
-SUP_EN(cfg_ms_sup_dcs, cfg_ms_sup_dcs_cmd, dcs, "dcs", "DCS (1800)", 1);
-SUP_DI(cfg_ms_sup_no_dcs, cfg_ms_sup_no_dcs_cmd, dcs, "dcs", "DCS (1800)", 1);
-SUP_EN(cfg_ms_sup_gsm_850, cfg_ms_sup_gsm_850_cmd, gsm_850, "gsm-850",
- "GSM 850", 1);
-SUP_DI(cfg_ms_sup_no_gsm_850, cfg_ms_sup_no_gsm_850_cmd, gsm_850, "gsm-850",
- "GSM 850", 1);
-SUP_EN(cfg_ms_sup_pcs, cfg_ms_sup_pcs_cmd, pcs, "pcs", "PCS (1900)", 1);
-SUP_DI(cfg_ms_sup_no_pcs, cfg_ms_sup_no_pcs_cmd, pcs, "pcs", "PCS (1900)", 1);
-SUP_EN(cfg_ms_sup_gsm_480, cfg_ms_sup_gsm_480_cmd, gsm_480, "gsm-480",
- "GSM 480", 1);
-SUP_DI(cfg_ms_sup_no_gsm_480, cfg_ms_sup_no_gsm_480_cmd, gsm_480, "gsm-480",
- "GSM 480", 1);
-SUP_EN(cfg_ms_sup_gsm_450, cfg_ms_sup_gsm_450_cmd, gsm_450, "gsm-450",
- "GSM 450", 1);
-SUP_DI(cfg_ms_sup_no_gsm_450, cfg_ms_sup_no_gsm_450_cmd, gsm_450, "gsm-450",
- "GSM 450", 1);
+#define SET_EN_DI(item, cmd, desc, restart) \
+ SET_EN(item, cmd, desc, restart); \
+ SET_DI(item, cmd, desc, restart)
+
+
+SET_EN_DI(cc_dtmf, "dtmf", "DTMF", 0);
+SUP_EN_DI(sms_ptp, "sms", "SMS", 0);
+SUP_EN_DI(a5_1, "a5/1", "A5/1", 0);
+SUP_EN_DI(a5_2, "a5/2", "A5/2", 0);
+SUP_EN_DI(a5_3, "a5/3", "A5/3", 0);
+SUP_EN_DI(a5_4, "a5/4", "A5/4", 0);
+SUP_EN_DI(a5_5, "a5/5", "A5/5", 0);
+SUP_EN_DI(a5_6, "a5/6", "A5/6", 0);
+SUP_EN_DI(a5_7, "a5/7", "A5/7", 0);
+SUP_EN_DI(p_gsm, "p-gsm", "P-GSM (900)", 1);
+SUP_EN_DI(e_gsm, "e-gsm", "E-GSM (850)", 1);
+SUP_EN_DI(r_gsm, "r-gsm", "R-GSM (850)", 1);
+SUP_EN_DI(dcs, "dcs", "DCS (1800)", 1);
+SUP_EN_DI(gsm_850, "gsm-850", "GSM 850", 1);
+SUP_EN_DI(pcs, "pcs", "PCS (1900)", 1);
+SUP_EN_DI(gsm_480, "gsm-480", "GSM 480", 1);
+SUP_EN_DI(gsm_450, "gsm-450", "GSM 450", 1);
DEFUN(cfg_ms_sup_class_900, cfg_ms_sup_class_900_cmd, "class-900 (1|2|3|4|5)",
"Select power class for GSM 900\n"
@@ -2342,7 +2490,7 @@ DEFUN(cfg_ms_sup_class_900, cfg_ms_sup_class_900_cmd, "class-900 (1|2|3|4|5)",
set->class_900 = atoi(argv[0]);
- if (set->class_900 < sup->class_900 && !vty_reading)
+ if (set->class_900 < sup->class_900 && !l23_vty_reading)
vty_out(vty, "Note: You selected a higher class than supported "
" by hardware!%s", VTY_NEWLINE);
@@ -2363,7 +2511,7 @@ DEFUN(cfg_ms_sup_class_850, cfg_ms_sup_class_850_cmd, "class-850 (1|2|3|4|5)",
set->class_850 = atoi(argv[0]);
- if (set->class_850 < sup->class_850 && !vty_reading)
+ if (set->class_850 < sup->class_850 && !l23_vty_reading)
vty_out(vty, "Note: You selected a higher class than supported "
" by hardware!%s", VTY_NEWLINE);
@@ -2384,7 +2532,7 @@ DEFUN(cfg_ms_sup_class_400, cfg_ms_sup_class_400_cmd, "class-400 (1|2|3|4|5)",
set->class_400 = atoi(argv[0]);
- if (set->class_400 < sup->class_400 && !vty_reading)
+ if (set->class_400 < sup->class_400 && !l23_vty_reading)
vty_out(vty, "Note: You selected a higher class than supported "
" by hardware!%s", VTY_NEWLINE);
@@ -2404,7 +2552,7 @@ DEFUN(cfg_ms_sup_class_dcs, cfg_ms_sup_class_dcs_cmd, "class-dcs (1|2|3)",
set->class_dcs = atoi(argv[0]);
if (((set->class_dcs + 1) & 3) < ((sup->class_dcs + 1) & 3)
- && !vty_reading)
+ && !l23_vty_reading)
vty_out(vty, "Note: You selected a higher class than supported "
" by hardware!%s", VTY_NEWLINE);
@@ -2424,7 +2572,7 @@ DEFUN(cfg_ms_sup_class_pcs, cfg_ms_sup_class_pcs_cmd, "class-pcs (1|2|3)",
set->class_pcs = atoi(argv[0]);
if (((set->class_pcs + 1) & 3) < ((sup->class_pcs + 1) & 3)
- && !vty_reading)
+ && !l23_vty_reading)
vty_out(vty, "Note: You selected a higher class than supported "
" by hardware!%s", VTY_NEWLINE);
@@ -2450,7 +2598,7 @@ DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd,
else
ch_cap = GSM_CAP_SDCCH;
- if (ch_cap > sup->ch_cap && !vty_reading) {
+ if (ch_cap > sup->ch_cap && !l23_vty_reading) {
vty_out(vty, "You selected an higher capability than supported "
" by hardware!%s", VTY_NEWLINE);
return CMD_WARNING;
@@ -2465,26 +2613,18 @@ DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd,
return CMD_SUCCESS;
}
-SUP_EN(cfg_ms_sup_full_v1, cfg_ms_sup_full_v1_cmd, full_v1, "full-speech-v1",
- "Full rate speech V1", 0);
-SUP_DI(cfg_ms_sup_no_full_v1, cfg_ms_sup_no_full_v1_cmd, full_v1,
- "full-speech-v1", "Full rate speech V1", 0);
-SUP_EN(cfg_ms_sup_full_v2, cfg_ms_sup_full_v2_cmd, full_v2, "full-speech-v2",
- "Full rate speech V2 (EFR)", 0);
-SUP_DI(cfg_ms_sup_no_full_v2, cfg_ms_sup_no_full_v2_cmd, full_v2,
- "full-speech-v2", "Full rate speech V2 (EFR)", 0);
-SUP_EN(cfg_ms_sup_full_v3, cfg_ms_sup_full_v3_cmd, full_v3, "full-speech-v3",
- "Full rate speech V3 (AMR)", 0);
-SUP_DI(cfg_ms_sup_no_full_v3, cfg_ms_sup_no_full_v3_cmd, full_v3,
- "full-speech-v3", "Full rate speech V3 (AMR)", 0);
-SUP_EN(cfg_ms_sup_half_v1, cfg_ms_sup_half_v1_cmd, half_v1, "half-speech-v1",
- "Half rate speech V1", 0);
-SUP_DI(cfg_ms_sup_no_half_v1, cfg_ms_sup_no_half_v1_cmd, half_v1,
- "half-speech-v1", "Half rate speech V1", 0);
-SUP_EN(cfg_ms_sup_half_v3, cfg_ms_sup_half_v3_cmd, half_v3, "half-speech-v3",
- "Half rate speech V3 (AMR)", 0);
-SUP_DI(cfg_ms_sup_no_half_v3, cfg_ms_sup_no_half_v3_cmd, half_v3,
- "half-speech-v3", "Half rate speech V3 (AMR)", 0);
+SUP_EN_DI(full_v1, "full-speech-v1", "Full rate speech V1", 0);
+SUP_EN_DI(full_v2, "full-speech-v2", "Full rate speech V2 (EFR)", 0);
+SUP_EN_DI(full_v3, "full-speech-v3", "Full rate speech V3 (AMR)", 0);
+SUP_EN_DI(half_v1, "half-speech-v1", "Half rate speech V1", 0);
+SUP_EN_DI(half_v3, "half-speech-v3", "Half rate speech V3 (AMR)", 0);
+
+SUP_EN_DI(csd_tch_f144, "full-data-14400", "CSD TCH/F14.4", 0);
+SUP_EN_DI(csd_tch_f96, "full-data-9600", "CSD TCH/F9.6", 0);
+SUP_EN_DI(csd_tch_f48, "full-data-4800", "CSD TCH/F4.8", 0);
+SUP_EN_DI(csd_tch_h48, "half-data-4800", "CSD TCH/H4.8", 0);
+SUP_EN_DI(csd_tch_f24, "full-data-2400", "CSD TCH/F2.4", 0);
+SUP_EN_DI(csd_tch_h24, "half-data-2400", "CSD TCH/H2.4", 0);
DEFUN(cfg_ms_sup_min_rxlev, cfg_ms_sup_min_rxlev_cmd, "min-rxlev <-110--47>",
"Set the minimum receive level to select a cell\n"
@@ -2536,291 +2676,184 @@ DEFUN(cfg_ms_sup_no_skip_max_per_band, cfg_ms_sup_no_skip_max_per_band_cmd,
return CMD_SUCCESS;
}
-/* per testsim config */
-DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim",
- "Configure test SIM emulation")
-{
- vty->node = TESTSIM_NODE;
+SUP_EN_DI(vgcs, "vgcs", "Voice Group Call Service (VGCS)", 0);
+SUP_EN_DI(vbs, "vbs", "Voice Broadcast Service (VBS)", 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI",
- "Set IMSI on test card\n15 digits IMSI")
+/* TCH config */
+DEFUN(cfg_ms_tch_voice,
+ cfg_ms_tch_voice_cmd,
+ "tch-voice", "Configure TCH (Traffic CHannel) params for voice calls\n")
{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
- char *error = gsm_check_imsi(argv[0]);
-
- if (error) {
- vty_out(vty, "%s%s", error, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- strcpy(set->test_imsi, argv[0]);
-
- vty_restart_if_started(vty, ms);
-
+ vty->node = TCH_VOICE_NODE;
return CMD_SUCCESS;
}
-#define HEX_STR "\nByte as two digits hexadecimal"
-DEFUN(cfg_test_ki_xor, cfg_test_ki_xor_cmd, "ki xor HEX HEX HEX HEX HEX HEX "
- "HEX HEX HEX HEX HEX HEX",
- "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR
- HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR)
-{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
- uint8_t ki[12];
- const char *p;
- int i;
-
- for (i = 0; i < 12; i++) {
- p = argv[i];
- if (!strncmp(p, "0x", 2))
- p += 2;
- if (strlen(p) != 2) {
- vty_out(vty, "Expecting two digits hex value (with or "
- "without 0x in front)%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- ki[i] = strtoul(p, NULL, 16);
- }
+ALIAS_DEPRECATED(cfg_ms_tch_voice, /* alias to 'tch-voice' */
+ cfg_ms_audio_cmd,
+ "audio", "(deprecated alias for 'tch-voice')\n");
- set->test_ki_type = OSMO_AUTH_ALG_XOR;
- memcpy(set->test_ki, ki, 12);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_test_ki_comp128, cfg_test_ki_comp128_cmd, "ki comp128 HEX HEX HEX "
- "HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX",
- "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR
- HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR
- HEX_STR HEX_STR HEX_STR HEX_STR)
+DEFUN(cfg_ms_tch_voice_io_handler, cfg_ms_tch_voice_io_handler_cmd,
+ "io-handler (none|gapk|l1phy|mncc-sock|loopback)",
+ "Set TCH frame I/O handler for voice calls\n"
+ "No handler, drop TCH frames (default)\n"
+ "libosmo-gapk based I/O handler (requires ALSA)\n"
+ "L1 PHY (e.g. Calypso DSP in Motorola C1xx phones)\n"
+ "External MNCC application (e.g. LCR) via MNCC socket\n"
+ "Return TCH frame payload back to sender\n")
{
- struct osmocom_ms *ms = vty->index;
+ int val = get_string_value(tch_voice_io_handler_names, argv[0]);
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
struct gsm_settings *set = &ms->settings;
- uint8_t ki[16];
- const char *p;
- int i;
- for (i = 0; i < 16; i++) {
- p = argv[i];
- if (!strncmp(p, "0x", 2))
- p += 2;
- if (strlen(p) != 2) {
- vty_out(vty, "Expecting two digits hex value (with or "
- "without 0x in front)%s", VTY_NEWLINE);
+ OSMO_ASSERT(val >= 0);
+
+ if (val == TCH_VOICE_IOH_MNCC_SOCK) {
+ if (ms->settings.mncc_handler != MNCC_HANDLER_INTERNAL) {
+ vty_out(vty, "TCH voice I/O handler 'mncc-sock' can only be used "
+ "with MNCC handler 'external'%s", VTY_NEWLINE);
return CMD_WARNING;
}
- ki[i] = strtoul(p, NULL, 16);
}
- set->test_ki_type = OSMO_AUTH_ALG_COMP128v1;
- memcpy(set->test_ki, ki, 16);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access",
- "Allow access to barred cells")
-{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
+#ifndef WITH_GAPK_IO
+ if (val == TCH_VOICE_IOH_GAPK) {
+ vty_out(vty, "GAPK I/O is not compiled in (--with-gapk-io)%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+#endif
- set->test_barr = 1;
+ set->tch_voice.io_handler = (enum tch_voice_io_handler)val;
return CMD_SUCCESS;
}
-DEFUN(cfg_test_no_barr, cfg_test_no_barr_cmd, "no barred-access",
- NO_STR "Deny access to barred cells")
+DEFUN(cfg_ms_tch_voice_no_io_handler, cfg_ms_tch_voice_no_io_handler_cmd,
+ "no io-handler", NO_STR "Disable TCH frame handling for voice calls\n")
{
- struct osmocom_ms *ms = vty->index;
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
struct gsm_settings *set = &ms->settings;
- set->test_barr = 0;
+ set->tch_voice.io_handler = TCH_VOICE_IOH_NONE;
return CMD_SUCCESS;
}
-DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn",
- NO_STR "Unset Registered PLMN")
+DEFUN(cfg_ms_tch_voice_io_tch_format, cfg_ms_tch_voice_io_tch_format_cmd,
+ "io-tch-format (rtp|ti)",
+ "Set TCH I/O frame format used by the L1 PHY (for GAPK only)\n"
+ "RTP format (RFC3551 for FR/EFR, RFC5993 for HR, RFC4867 for AMR)\n"
+ "Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx)\n")
{
- struct osmocom_ms *ms = vty->index;
+ int val = get_string_value(tch_voice_io_format_names, argv[0]);
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
struct gsm_settings *set = &ms->settings;
- set->test_rplmn_valid = 0;
+ OSMO_ASSERT(val >= 0);
- vty_restart_if_started(vty, ms);
+ if (set->tch_voice.io_handler != TCH_VOICE_IOH_GAPK) {
+ vty_out(vty, "This parameter is only valid for GAPK%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ set->tch_voice.io_format = val;
return CMD_SUCCESS;
}
-static int _test_rplmn_cmd(struct vty *vty, int argc, const char *argv[],
- int attached)
+DEFUN(cfg_ms_tch_voice_alsa_out_dev, cfg_ms_tch_voice_alsa_out_dev_cmd,
+ "alsa-output-dev (default|NAME)",
+ "Set ALSA output (playback) device name (for GAPK only)\n"
+ "Default system playback device (default)\n"
+ "Name of a custom playback device")
{
struct osmocom_ms *ms = vty->index;
struct gsm_settings *set = &ms->settings;
- uint16_t mcc = gsm_input_mcc((char *)argv[0]),
- mnc = gsm_input_mnc((char *)argv[1]);
-
- if (mcc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (mnc == GSM_INPUT_INVALID) {
- vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- set->test_rplmn_valid = 1;
- set->test_rplmn_mcc = mcc;
- set->test_rplmn_mnc = mnc;
-
- if (argc >= 3)
- set->test_lac = strtoul(argv[2], NULL, 16);
- else
- set->test_lac = 0xfffe;
-
- if (argc >= 4)
- set->test_tmsi = strtoul(argv[3], NULL, 16);
- else
- set->test_tmsi = 0xffffffff;
- if (attached)
- set->test_imsi_attached = 1;
- else
- set->test_imsi_attached = 0;
-
- vty_restart_if_started(vty, ms);
+ OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_output_dev, argv[0]);
return CMD_SUCCESS;
}
-DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd,
- "rplmn MCC MNC [LAC] [TMSI]",
- "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n"
- "Optionally set location area code\n"
- "Optionally set current assigned TMSI")
-{
- return _test_rplmn_cmd(vty, argc, argv, 0);
-}
-
-DEFUN(cfg_test_rplmn_att, cfg_test_rplmn_att_cmd,
- "rplmn MCC MNC LAC TMSI attached",
- "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n"
- "Set location area code\nSet current assigned TMSI\n"
- "Indicate to MM that card is already attached")
-{
- return _test_rplmn_cmd(vty, argc, argv, 1);
-}
-
-DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-country)",
- "Set Home PLMN search mode\n"
- "Search for HPLMN when on any other network\n"
- "Search for HPLMN when in a different country")
+DEFUN(cfg_ms_tch_voice_alsa_in_dev, cfg_ms_tch_voice_alsa_in_dev_cmd,
+ "alsa-input-dev (default|NAME)",
+ "Set ALSA input (capture) device name (for GAPK only)\n"
+ "Default system recording device (default)\n"
+ "Name of a custom recording device")
{
struct osmocom_ms *ms = vty->index;
struct gsm_settings *set = &ms->settings;
- switch (argv[0][0]) {
- case 'e':
- set->test_always = 1;
- break;
- case 'f':
- set->test_always = 0;
- break;
- }
-
- vty_restart_if_started(vty, ms);
+ OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_input_dev, argv[0]);
return CMD_SUCCESS;
}
-/* per audio config */
-DEFUN(cfg_ms_audio, cfg_ms_audio_cmd, "audio",
- "Configure audio settings")
+DEFUN(cfg_ms_tch_data,
+ cfg_ms_tch_data_cmd,
+ "tch-data", "Configure TCH (Traffic CHannel) params for data calls\n")
{
- vty->node = AUDIO_NODE;
+ vty->node = TCH_DATA_NODE;
return CMD_SUCCESS;
}
-static int set_audio_io_handler(struct vty *vty, enum audio_io_handler val)
+DEFUN(cfg_ms_tch_data_io_handler,
+ cfg_ms_tch_data_io_handler_cmd,
+ "io-handler (none|unix-sock|loopback)",
+ "Set TCH frame I/O handler for data calls\n"
+ "No handler, drop TCH frames (default)\n"
+ "UNIX socket (path set by 'data-unix-socket')\n"
+ "Return TCH frame payload back to sender\n")
{
- struct osmocom_ms *ms = (struct osmocom_ms *) vty->index;
+ int val = get_string_value(tch_data_io_handler_names, argv[0]);
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
struct gsm_settings *set = &ms->settings;
- /* Don't restart on unchanged value */
- if (val == set->audio.io_handler)
- return CMD_SUCCESS;
- set->audio.io_handler = val;
-
- /* Restart required */
- vty_restart_if_started(vty, ms);
+ OSMO_ASSERT(val >= 0);
+ set->tch_data.io_handler = (enum tch_data_io_handler)val;
return CMD_SUCCESS;
}
-DEFUN(cfg_ms_audio_io_handler, cfg_ms_audio_io_handler_cmd,
- "io-handler (loopback|none)",
- "Set TCH frame I/O handler\n"
- "Return TCH frame payload back to sender\n"
- "No handler, drop TCH frames (default)")
+DEFUN(cfg_ms_tch_data_no_io_handler,
+ cfg_ms_tch_data_no_io_handler_cmd,
+ "no io-handler", NO_STR "Disable TCH frame handling for data calls\n")
{
- int val = get_string_value(audio_io_handler_names, argv[0]);
- return set_audio_io_handler(vty, val);
-}
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct gsm_settings *set = &ms->settings;
-DEFUN(cfg_ms_audio_no_io_handler, cfg_ms_audio_no_io_handler_cmd,
- "no io-handler", NO_STR "Disable TCH frame processing")
-{
- return set_audio_io_handler(vty, AUDIO_IOH_NONE);
+ set->tch_data.io_handler = TCH_DATA_IOH_NONE;
+
+ return CMD_SUCCESS;
}
-DEFUN(cfg_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown",
- NO_STR "Activate and run MS")
+DEFUN(cfg_ms_tch_data_io_tch_format,
+ cfg_ms_tch_data_io_tch_format_cmd,
+ "io-tch-format (osmo|ti)",
+ "Set TCH I/O frame format used by the L1 PHY\n"
+ "Osmocom format used by both trxcon and viryphy (default)\n"
+ "Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx)\n")
{
- struct osmocom_ms *ms = vty->index;
- char *other_name = NULL;
- int rc;
+ int val = get_string_value(tch_data_io_format_names, argv[0]);
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct gsm_settings *set = &ms->settings;
- rc = mobile_start(ms, &other_name);
- switch (rc) {
- case -1:
- vty_out(vty, "Cannot start MS '%s', because MS '%s' "
- "use the same layer2-socket.%sPlease shutdown "
- "MS '%s' first.%s", ms->name, other_name,
- VTY_NEWLINE, other_name, VTY_NEWLINE);
- return CMD_WARNING;
- case -2:
- vty_out(vty, "Cannot start MS '%s', because MS '%s' "
- "use the same sap-socket.%sPlease shutdown "
- "MS '%s' first.%s", ms->name, other_name,
- VTY_NEWLINE, other_name, VTY_NEWLINE);
- return CMD_WARNING;
- case -3:
- vty_out(vty, "Connection to layer 1 failed!%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
+ OSMO_ASSERT(val >= 0);
+ set->tch_data.io_format = val;
return CMD_SUCCESS;
}
-DEFUN(cfg_shutdown, cfg_ms_shutdown_cmd, "shutdown",
- "Shut down and deactivate MS")
+DEFUN(cfg_ms_tch_data_unix_sock,
+ cfg_ms_tch_data_unix_sock_cmd,
+ "unix-socket PATH",
+ "Define UNIX socket path (for 'io-handler unix-sock')\n"
+ "UNIX socket path (default '/tmp/ms_data_' + MS_NAME)\n")
{
- struct osmocom_ms *ms = vty->index;
- mobile_stop(ms, 0);
- return CMD_SUCCESS;
-}
+ struct osmocom_ms *ms = (struct osmocom_ms *)vty->index;
+ struct gsm_settings *set = &ms->settings;
-DEFUN(cfg_shutdown_force, cfg_ms_shutdown_force_cmd, "shutdown force",
- "Shut down and deactivate MS\nDo not perform IMSI detach")
-{
- struct osmocom_ms *ms = vty->index;
+ OSMO_STRLCPY_ARRAY(set->tch_data.unix_socket_path, argv[0]);
- mobile_stop(ms, 1);
return CMD_SUCCESS;
}
@@ -2848,25 +2881,6 @@ DEFUN(cfg_ms_no_script_load_run, cfg_ms_no_script_load_run_cmd, "no lua-script",
return CMD_SUCCESS;
}
-int ms_vty_go_parent(struct vty *vty)
-{
- switch (vty->node) {
- case MS_NODE:
- vty->node = CONFIG_NODE;
- vty->index = NULL;
- break;
- case TESTSIM_NODE:
- case SUPPORT_NODE:
- case AUDIO_NODE:
- vty->node = MS_NODE;
- break;
- default:
- vty->node = CONFIG_NODE;
- }
-
- return vty->node;
-}
-
DEFUN(off, off_cmd, "off",
"Turn mobiles off (shutdown) and exit")
{
@@ -2875,43 +2889,138 @@ DEFUN(off, off_cmd, "off",
return CMD_SUCCESS;
}
+/* run ms instance, if layer1 is available */
+static int l23_vty_signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmobb_l23_vty_sig_data *d = signal_data;
+ struct vty *vty = d->vty;
+ char *other_name = NULL;
+ int rc;
+
+ if (subsys != SS_L23_VTY)
+ return 0;
+
+ switch (signal) {
+ case S_L23_VTY_MS_START:
+ rc = mobile_start(d->ms_start.ms, &other_name);
+ switch (rc) {
+ case -1:
+ vty_out(vty, "Cannot start MS '%s', because MS '%s' "
+ "use the same layer2-socket.%sPlease shutdown "
+ "MS '%s' first.%s", d->ms_start.ms->name, other_name,
+ VTY_NEWLINE, other_name, VTY_NEWLINE);
+ break;
+ case -2:
+ vty_out(vty, "Cannot start MS '%s', because MS '%s' "
+ "use the same sap-socket.%sPlease shutdown "
+ "MS '%s' first.%s", d->ms_start.ms->name, other_name,
+ VTY_NEWLINE, other_name, VTY_NEWLINE);
+ break;
+ case -3:
+ vty_out(vty, "Connection to layer 1 failed!%s",
+ VTY_NEWLINE);
+ break;
+ }
+ d->ms_start.rc = (rc == 0) ? CMD_SUCCESS : CMD_WARNING;
+ break;
+ case S_L23_VTY_MS_STOP:
+ mobile_stop(d->ms_stop.ms, d->ms_stop.force);
+ d->ms_start.rc = CMD_SUCCESS;
+ break;
+ }
+ return 0;
+}
+
+
#define SUP_NODE(item) \
install_element(SUPPORT_NODE, &cfg_ms_sup_item_cmd);
int ms_vty_init(void)
{
+ int rc;
+
+ _data_type_rate_cmd_string(NULL, &cfg_ms_tch_data_cp_type_rate_cmd);
+ _data_type_rate_cmd_string(NULL, &call_params_data_type_rate_cmd);
+
+ cfg_ms_tch_data_cp_type_rate_cmd.doc =
+ vty_cmd_string_from_valstr(NULL,
+ data_type_rate_descs,
+ CFG_TCH_DATA_CALL_PARAMS_CMD_DESC
+ "Type and rate (values like in AT+CBST; "
+ "see 3GPP TS 27.007, section 6.7)\n",
+ "\n", "", 0);
+ call_params_data_type_rate_cmd.doc =
+ vty_cmd_string_from_valstr(NULL,
+ data_type_rate_descs,
+ CALL_PARAMS_DATA_CMD_DESC
+ "Type and rate (values like in AT+CBST; "
+ "see 3GPP TS 27.007, section 6.7)\n",
+ "\n", "", 0);
+
+ cfg_ms_tch_data_cp_async_parity_cmd.string =
+ vty_cmd_string_from_valstr(NULL,
+ async_parity_names,
+ CFG_TCH_DATA_CALL_PARAMS_CMD " "
+ CALL_PARAMS_ASYNC_PARITY_CMD " (",
+ "|", ")", 0);
+ call_params_data_async_parity_cmd.string =
+ vty_cmd_string_from_valstr(NULL,
+ async_parity_names,
+ CALL_PARAMS_DATA_CMD " "
+ CALL_PARAMS_ASYNC_PARITY_CMD " (",
+ "|", ")", 0);
+
+ cfg_ms_tch_data_cp_async_parity_cmd.doc =
+ vty_cmd_string_from_valstr(NULL,
+ async_parity_descs,
+ cfg_ms_tch_data_cp_async_parity_cmd.doc,
+ "\n", "", 0);
+ call_params_data_async_parity_cmd.doc =
+ vty_cmd_string_from_valstr(NULL,
+ async_parity_descs,
+ call_params_data_async_parity_cmd.doc,
+ "\n", "", 0);
+
+ if ((rc = l23_vty_init(config_write, l23_vty_signal_cb)) < 0)
+ return rc;
+
install_element_ve(&show_ms_cmd);
- install_element_ve(&show_subscr_cmd);
- install_element_ve(&show_support_cmd);
install_element_ve(&show_cell_cmd);
install_element_ve(&show_cell_si_cmd);
install_element_ve(&show_nbcells_cmd);
install_element_ve(&show_ba_cmd);
install_element_ve(&show_forb_la_cmd);
install_element_ve(&show_forb_plmn_cmd);
+ install_element_ve(&show_asci_calls_cmd);
+ install_element_ve(&show_asci_neighbors_cmd);
install_element_ve(&monitor_network_cmd);
install_element_ve(&no_monitor_network_cmd);
install_element(ENABLE_NODE, &off_cmd);
- install_element(ENABLE_NODE, &sim_test_cmd);
- install_element(ENABLE_NODE, &sim_test_att_cmd);
- install_element(ENABLE_NODE, &sim_sap_cmd);
- install_element(ENABLE_NODE, &sim_reader_cmd);
- install_element(ENABLE_NODE, &sim_remove_cmd);
- install_element(ENABLE_NODE, &sim_pin_cmd);
- install_element(ENABLE_NODE, &sim_disable_pin_cmd);
- install_element(ENABLE_NODE, &sim_enable_pin_cmd);
- install_element(ENABLE_NODE, &sim_change_pin_cmd);
- install_element(ENABLE_NODE, &sim_unblock_pin_cmd);
- install_element(ENABLE_NODE, &sim_lai_cmd);
install_element(ENABLE_NODE, &network_search_cmd);
install_element(ENABLE_NODE, &network_show_cmd);
install_element(ENABLE_NODE, &network_select_cmd);
+ install_element(ENABLE_NODE, &call_num_cmd);
install_element(ENABLE_NODE, &call_cmd);
install_element(ENABLE_NODE, &call_retr_cmd);
install_element(ENABLE_NODE, &call_dtmf_cmd);
+ install_element(ENABLE_NODE, &call_params_data_type_rate_cmd);
+ install_element(ENABLE_NODE, &call_params_data_ce_cmd);
+ install_element(ENABLE_NODE, &call_params_data_sync_async_cmd);
+ install_element(ENABLE_NODE, &call_params_data_async_nr_stop_bits_cmd);
+ install_element(ENABLE_NODE, &call_params_data_async_nr_data_bits_cmd);
+ install_element(ENABLE_NODE, &call_params_data_async_parity_cmd);
install_element(ENABLE_NODE, &sms_cmd);
install_element(ENABLE_NODE, &service_cmd);
+ install_element(ENABLE_NODE, &vgcs_enter_cmd);
+ install_element(ENABLE_NODE, &vgcs_direct_cmd);
+ install_node(&vgcs_node, config_write_dummy);
+ install_element(VGCS_NODE, &vgcs_cmd);
+ install_element(ENABLE_NODE, &vbs_enter_cmd);
+ install_element(ENABLE_NODE, &vbs_direct_cmd);
+ install_node(&vbs_node, config_write_dummy);
+ install_element(VBS_NODE, &vbs_cmd);
install_element(ENABLE_NODE, &test_reselection_cmd);
install_element(ENABLE_NODE, &delete_forbidden_plmn_cmd);
@@ -2923,22 +3032,18 @@ int ms_vty_init(void)
install_element(CONFIG_NODE, &cfg_gps_enable_cmd);
install_element(CONFIG_NODE, &cfg_no_gps_enable_cmd);
- install_element(CONFIG_NODE, &cfg_hide_default_cmd);
- install_element(CONFIG_NODE, &cfg_no_hide_default_cmd);
-
install_element(CONFIG_NODE, &cfg_ms_cmd);
install_element(CONFIG_NODE, &cfg_ms_create_cmd);
install_element(CONFIG_NODE, &cfg_ms_rename_cmd);
install_element(CONFIG_NODE, &cfg_no_ms_cmd);
- install_node(&ms_node, config_write);
+
+ /* MS_NODE is installed by l23_vty_init(). App specific commands below: */
install_element(MS_NODE, &cfg_ms_show_this_cmd);
- install_element(MS_NODE, &cfg_ms_layer2_cmd);
install_element(MS_NODE, &cfg_ms_sap_cmd);
- install_element(MS_NODE, &cfg_ms_sim_cmd);
+ install_element(MS_NODE, &cfg_ms_mncc_sock_cmd);
+ install_element(MS_NODE, &cfg_ms_mncc_handler_cmd);
+ install_element(MS_NODE, &cfg_ms_no_mncc_handler_cmd);
install_element(MS_NODE, &cfg_ms_mode_cmd);
- install_element(MS_NODE, &cfg_ms_imei_cmd);
- install_element(MS_NODE, &cfg_ms_imei_fixed_cmd);
- install_element(MS_NODE, &cfg_ms_imei_random_cmd);
install_element(MS_NODE, &cfg_ms_no_emerg_imsi_cmd);
install_element(MS_NODE, &cfg_ms_emerg_imsi_cmd);
install_element(MS_NODE, &cfg_ms_no_sms_sca_cmd);
@@ -2968,127 +3073,112 @@ int ms_vty_init(void)
install_element(MS_NODE, &cfg_ms_no_codec_half_cmd);
install_element(MS_NODE, &cfg_ms_abbrev_cmd);
install_element(MS_NODE, &cfg_ms_no_abbrev_cmd);
- install_element(MS_NODE, &cfg_ms_testsim_cmd);
+ install_element(MS_NODE, &cfg_ms_tch_voice_cmd);
install_element(MS_NODE, &cfg_ms_audio_cmd);
+ install_element(MS_NODE, &cfg_ms_tch_data_cmd);
install_element(MS_NODE, &cfg_ms_neighbour_cmd);
install_element(MS_NODE, &cfg_ms_no_neighbour_cmd);
install_element(MS_NODE, &cfg_ms_any_timeout_cmd);
install_element(MS_NODE, &cfg_ms_sms_store_cmd);
install_element(MS_NODE, &cfg_ms_no_sms_store_cmd);
+ install_element(MS_NODE, &cfg_ms_uplink_release_local_cmd);
+ install_element(MS_NODE, &cfg_ms_no_uplink_release_local_cmd);
+ install_element(MS_NODE, &cfg_ms_asci_allow_any_cmd);
+ install_element(MS_NODE, &cfg_ms_no_asci_allow_any_cmd);
install_element(MS_NODE, &cfg_ms_support_cmd);
install_node(&support_node, config_write_dummy);
- install_element(SUPPORT_NODE, &cfg_ms_sup_dtmf_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_dtmf_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_sms_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_sms_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_1_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_1_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_2_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_2_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_3_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_3_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_4_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_4_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_5_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_5_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_6_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_6_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_a5_7_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_7_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_p_gsm_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_p_gsm_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_e_gsm_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_e_gsm_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_r_gsm_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_r_gsm_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_dcs_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_dcs_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_850_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_850_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_pcs_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_pcs_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_480_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_480_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_450_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_450_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_set_en_cc_dtmf_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_set_di_cc_dtmf_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_sms_ptp_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_sms_ptp_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_1_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_1_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_2_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_2_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_4_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_4_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_5_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_5_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_6_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_6_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_7_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_7_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_p_gsm_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_p_gsm_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_e_gsm_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_e_gsm_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_r_gsm_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_r_gsm_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_dcs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_dcs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_gsm_850_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_gsm_850_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_pcs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_pcs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_gsm_480_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_gsm_480_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_gsm_450_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_gsm_450_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_class_900_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_class_dcs_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_class_850_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_class_pcs_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_class_400_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_ch_cap_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_full_v1_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v1_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_full_v2_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v2_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_full_v3_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v3_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_half_v1_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v1_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_half_v3_cmd);
- install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_full_v1_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_full_v1_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_full_v2_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_full_v2_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_full_v3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_full_v3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_half_v1_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_half_v1_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_half_v3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_half_v3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f144_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f144_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f96_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f96_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_h48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_h48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f24_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f24_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_h24_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_h24_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_min_rxlev_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_dsc_max_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_skip_max_per_band_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_no_skip_max_per_band_cmd);
- install_node(&testsim_node, config_write_dummy);
- install_element(TESTSIM_NODE, &cfg_test_imsi_cmd);
- install_element(TESTSIM_NODE, &cfg_test_ki_xor_cmd);
- install_element(TESTSIM_NODE, &cfg_test_ki_comp128_cmd);
- install_element(TESTSIM_NODE, &cfg_test_barr_cmd);
- install_element(TESTSIM_NODE, &cfg_test_no_barr_cmd);
- install_element(TESTSIM_NODE, &cfg_test_no_rplmn_cmd);
- install_element(TESTSIM_NODE, &cfg_test_rplmn_cmd);
- install_element(TESTSIM_NODE, &cfg_test_rplmn_att_cmd);
- install_element(TESTSIM_NODE, &cfg_test_hplmn_cmd);
- install_element(MS_NODE, &cfg_ms_shutdown_cmd);
- install_element(MS_NODE, &cfg_ms_shutdown_force_cmd);
- install_element(MS_NODE, &cfg_ms_no_shutdown_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_vgcs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_vgcs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_vbs_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_vbs_cmd);
install_element(MS_NODE, &cfg_ms_script_load_run_cmd);
install_element(MS_NODE, &cfg_ms_no_script_load_run_cmd);
- install_node(&audio_node, config_write_dummy);
- install_element(AUDIO_NODE, &cfg_ms_audio_io_handler_cmd);
- install_element(AUDIO_NODE, &cfg_ms_audio_no_io_handler_cmd);
-
- /* Register the talloc context introspection command */
- osmo_talloc_vty_add_cmds();
+ install_node(&tch_voice_node, config_write_dummy);
+ install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_io_handler_cmd);
+ install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_no_io_handler_cmd);
+ install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_io_tch_format_cmd);
+ install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_alsa_out_dev_cmd);
+ install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_alsa_in_dev_cmd);
+
+ install_node(&tch_data_node, config_write_dummy);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_io_handler_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_no_io_handler_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_io_tch_format_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_unix_sock_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_type_rate_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_ce_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_sync_async_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_async_nr_stop_bits_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_async_nr_data_bits_cmd);
+ install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_async_parity_cmd);
return 0;
}
-void vty_notify(struct osmocom_ms *ms, const char *fmt, ...)
-{
- struct telnet_connection *connection;
- char buffer[1000];
- va_list args;
- struct vty *vty;
-
- if (fmt) {
- va_start(args, fmt);
- vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
- buffer[sizeof(buffer) - 1] = '\0';
- va_end(args);
-
- if (!buffer[0])
- return;
- }
-
- llist_for_each_entry(connection, &active_connections, entry) {
- vty = connection->vty;
- if (!vty)
- continue;
- if (!fmt) {
- vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name,
- VTY_NEWLINE);
- continue;
- }
- if (buffer[strlen(buffer) - 1] == '\n') {
- buffer[strlen(buffer) - 1] = '\0';
- vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE);
- buffer[strlen(buffer)] = '\n';
- } else
- vty_out(vty, "%% %s", buffer);
- }
-}
-
diff --git a/src/host/layer23/src/modem/Makefile.am b/src/host/layer23/src/modem/Makefile.am
new file mode 100644
index 00000000..935722b1
--- /dev/null
+++ b/src/host/layer23/src/modem/Makefile.am
@@ -0,0 +1,41 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOGPRSRLCMAC_CFLAGS) \
+ $(LIBOSMOGPRSLLC_CFLAGS) \
+ $(LIBOSMOGPRSSNDCP_CFLAGS) \
+ $(LIBOSMOGPRSGMM_CFLAGS) \
+ $(LIBOSMOGPRSSM_CFLAGS) \
+ $(NULL)
+
+bin_PROGRAMS = modem
+
+modem_SOURCES = \
+ $(top_srcdir)/src/common/main.c \
+ app_modem.c \
+ gmm.c \
+ grr.c \
+ llc.c \
+ rlcmac.c \
+ sm.c \
+ sndcp.c \
+ vty.c \
+ $(NULL)
+modem_LDADD = \
+ $(top_builddir)/src/common/liblayer23.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOGPRSRLCMAC_LIBS) \
+ $(LIBOSMOGPRSLLC_LIBS) \
+ $(LIBOSMOGPRSSNDCP_LIBS) \
+ $(LIBOSMOGPRSGMM_LIBS) \
+ $(LIBOSMOGPRSSM_LIBS) \
+ $(NULL)
diff --git a/src/host/layer23/src/modem/app_modem.c b/src/host/layer23/src/modem/app_modem.c
new file mode 100644
index 00000000..2787ec70
--- /dev/null
+++ b/src/host/layer23/src/modem/app_modem.c
@@ -0,0 +1,348 @@
+/* modem app (gprs) */
+
+/* (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 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/lienses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/tun.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/vty/vty.h>
+
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/settings.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/l23_app.h>
+#include <osmocom/bb/common/l1l2_interface.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/modem/rlcmac.h>
+#include <osmocom/bb/modem/llc.h>
+#include <osmocom/bb/modem/sndcp.h>
+#include <osmocom/bb/modem/gmm.h>
+#include <osmocom/bb/modem/sm.h>
+#include <osmocom/bb/modem/vty.h>
+#include <osmocom/bb/modem/grr.h>
+#include <osmocom/bb/modem/modem.h>
+
+#include <l1ctl_proto.h>
+
+#include "config.h"
+
+struct modem_app app_data;
+
+int modem_gprs_attach_if_needed(struct osmocom_ms *ms)
+{
+ int rc;
+
+ if (app_data.modem_state != MODEM_ST_IDLE)
+ return 0;
+
+ if (ms->grr_fi->state == GRR_ST_PACKET_NOT_READY)
+ return 0;
+
+ if (!ms->subscr.sim_valid)
+ return 0;
+
+ app_data.modem_state = MODEM_ST_ATTACHING;
+ rc = modem_gmm_gmmreg_attach_req(ms);
+ if (rc < 0)
+ app_data.modem_state = MODEM_ST_IDLE;
+ return rc;
+}
+
+/* Local network-originated IP packet, needs to be sent via SNDCP/LLC (GPRS) towards GSM network */
+static int modem_tun_data_ind_cb(struct osmo_tundev *tun, struct msgb *msg)
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *)osmo_tundev_get_priv_data(tun);
+ struct osmo_sockaddr dst;
+ struct iphdr *iph = (struct iphdr *)msgb_data(msg);
+ struct ip6_hdr *ip6h = (struct ip6_hdr *)msgb_data(msg);
+ size_t pkt_len = msgb_length(msg);
+ uint8_t pref_offset;
+ char addrstr[INET6_ADDRSTRLEN];
+ int rc = 0;
+
+ switch (iph->version) {
+ case 4:
+ if (pkt_len < sizeof(*iph) || pkt_len < 4*iph->ihl)
+ return -1;
+ dst.u.sin.sin_family = AF_INET;
+ dst.u.sin.sin_addr.s_addr = iph->daddr;
+ break;
+ case 6:
+ /* Due to the fact that 3GPP requires an allocation of a
+ * /64 prefix to each MS, we must instruct
+ * ippool_getip() below to match only the leading /64
+ * prefix, i.e. the first 8 bytes of the address. If the ll addr
+ * is used, then the match should be done on the trailing 64
+ * bits. */
+ dst.u.sin6.sin6_family = AF_INET6;
+ pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0;
+ memcpy(&dst.u.sin6.sin6_addr, ((uint8_t *)&ip6h->ip6_dst) + pref_offset, 8);
+ break;
+ default:
+ LOGTUN(LOGL_NOTICE, tun, "non-IPv%u packet received\n", iph->version);
+ rc = -1;
+ goto free_ret;
+ }
+
+ LOGPAPN(LOGL_DEBUG, apn, "system wants to transmit IPv%c pkt to %s (%zu bytes)\n",
+ iph->version == 4 ? '4' : '6', osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len);
+
+ switch (apn->pdp.pdp_addr_ietf_type) {
+ case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4:
+ if (iph->version != 4) {
+ LOGPAPN(LOGL_NOTICE, apn,
+ "system wants to transmit IPv%u pkt to %s (%zu bytes) on IPv4-only PDP Ctx, discarding!\n",
+ iph->version, osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len);
+ goto free_ret;
+ }
+ break;
+ case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6:
+ if (iph->version != 6) {
+ LOGPAPN(LOGL_NOTICE, apn,
+ "system wants to transmit IPv%u pkt to %s (%zu bytes) on IPv6-only PDP Ctx, discarding!\n",
+ iph->version, osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len);
+ goto free_ret;
+ }
+ break;
+ default: /* OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6 */
+ /* Allow any */
+ break;
+ }
+
+ rc = modem_sndcp_sn_unitdata_req(apn, msgb_data(msg), pkt_len);
+
+free_ret:
+ msgb_free(msg);
+ return rc;
+}
+
+void layer3_app_reset(void)
+{
+ memset(&app_data, 0x00, sizeof(app_data));
+}
+
+/* SIM becomes ATTACHED/DETACHED, or answers a request */
+static int modem_l23_subscr_signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+ struct osmobb_l23_subscr_sim_auth_resp_sig_data *sim_auth_resp;
+
+ OSMO_ASSERT(subsys == SS_L23_SUBSCR);
+
+ switch (signal) {
+ case S_L23_SUBSCR_SIM_ATTACHED:
+ ms = signal_data;
+ modem_gprs_attach_if_needed(ms);
+ break;
+ case S_L23_SUBSCR_SIM_DETACHED:
+ ms = signal_data;
+ modem_gmm_gmmreg_detach_req(ms);
+ break;
+ case S_L23_SUBSCR_SIM_AUTH_RESP:
+ sim_auth_resp = signal_data;
+ ms = sim_auth_resp->ms;
+ modem_gmm_gmmreg_sim_auth_rsp(ms, sim_auth_resp->sres,
+ ms->subscr.key,
+ sizeof(ms->subscr.key));
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
+int modem_sync_to_cell(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ if (cs->sync_pending) {
+ LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%s, but there is a sync "
+ "already pending\n", gsm_print_arfcn(cs->arfcn));
+ return 0;
+ }
+
+ cs->sync_pending = true;
+ l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
+ return l1ctl_tx_fbsb_req(ms, cs->arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ cs->ccch_mode, dbm2rxlev(-85));
+}
+
+static int global_signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+ struct gsm322_cellsel *cs;
+ struct osmobb_fbsb_res *fr;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_RESET:
+ LOGP(DCS, LOGL_NOTICE, "S_L1CTL_RESET\n");
+ ms = signal_data;
+ ms->cellsel.arfcn = ms->test_arfcn;
+ if (ms->started)
+ break;
+ layer3_app_reset();
+ app_data.ms = ms;
+
+ /* insert test card, if enabled */
+ if (ms->settings.sim_type != GSM_SIM_TYPE_NONE) {
+ /* insert sim card */
+ gsm_subscr_insert(ms);
+ } else {
+ /* No SIM, trigger PLMN selection process.
+ * FIXME: not implemented. Code in mobile needs to be
+ * moved to common/ and reuse it here.
+ */
+ }
+
+ ms->started = true;
+ return l1ctl_tx_fbsb_req(ms, ms->test_arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ CCCH_MODE_NONE, dbm2rxlev(-85));
+ case S_L1CTL_FBSB_RESP:
+ LOGP(DCS, LOGL_NOTICE, "S_L1CTL_FBSB_RESP\n");
+ fr = signal_data;
+ ms = fr->ms;
+ cs = &ms->cellsel;
+ cs->sync_pending = false;
+ break;
+ case S_L1CTL_FBSB_ERR:
+ LOGP(DCS, LOGL_NOTICE, "S_L1CTL_FBSB_ERR\n");
+ fr = signal_data;
+ ms = fr->ms;
+ cs = &ms->cellsel;
+ cs->sync_pending = false;
+ /* Retry: */
+ modem_sync_to_cell(ms);
+ break;
+ }
+
+ return 0;
+}
+
+static int _modem_start(void)
+{
+ int rc;
+
+ rc = layer2_open(app_data.ms, app_data.ms->settings.layer2_socket_path);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during layer2_open()\n");
+ return rc;
+ }
+
+ l1ctl_tx_reset_req(app_data.ms, L1CTL_RES_T_FULL);
+ return 0;
+}
+
+/* global exit */
+static int _modem_exit(void)
+{
+ osmo_signal_unregister_handler(SS_L23_SUBSCR, &modem_l23_subscr_signal_cb, NULL);
+ osmo_signal_unregister_handler(SS_GLOBAL, &global_signal_cb, NULL);
+ return 0;
+}
+
+int l23_app_init(void)
+{
+ int rc;
+
+ l23_app_start = _modem_start;
+ l23_app_exit = _modem_exit;
+
+ log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
+ log_set_category_filter(osmo_stderr_target, DLCSN1, 1, LOGL_DEBUG);
+ log_set_category_filter(osmo_stderr_target, DRR, 1, LOGL_INFO);
+
+ app_data.ms = osmocom_ms_alloc(l23_ctx, "1");
+ OSMO_ASSERT(app_data.ms);
+
+ if ((rc = modem_rlcmac_init(app_data.ms))) {
+ LOGP(DRLCMAC, LOGL_FATAL, "Failed initializing RLC/MAC layer\n");
+ return rc;
+ }
+
+ if ((rc = modem_llc_init(app_data.ms, NULL))) {
+ LOGP(DLLC, LOGL_FATAL, "Failed initializing LLC layer\n");
+ return rc;
+ }
+
+ if ((rc = modem_sndcp_init(app_data.ms))) {
+ LOGP(DSNDCP, LOGL_FATAL, "Failed initializing SNDCP layer\n");
+ return rc;
+ }
+
+ if ((rc = modem_gmm_init(app_data.ms))) {
+ LOGP(DGMM, LOGL_FATAL, "Failed initializing GMM layer\n");
+ return rc;
+ }
+
+ if ((rc = modem_sm_init(app_data.ms))) {
+ LOGP(DSM, LOGL_FATAL, "Failed initializing SM layer\n");
+ return rc;
+ }
+
+ /* TODO: move to a separate function */
+ app_data.ms->grr_fi = osmo_fsm_inst_alloc(&grr_fsm_def, NULL,
+ app_data.ms, LOGL_DEBUG,
+ app_data.ms->name);
+ OSMO_ASSERT(app_data.ms->grr_fi != NULL);
+
+ osmo_signal_register_handler(SS_L1CTL, &global_signal_cb, NULL);
+ osmo_signal_register_handler(SS_L23_SUBSCR, &modem_l23_subscr_signal_cb, NULL);
+ lapdm_channel_set_l3(&app_data.ms->lapdm_channel, &modem_grr_rslms_cb, app_data.ms);
+ return 0;
+}
+
+static struct vty_app_info _modem_vty_info = {
+ .name = "OsmocomBB(modem)",
+ .version = PACKAGE_VERSION,
+ .go_parent_cb = modem_vty_go_parent,
+};
+
+const struct l23_app_info l23_app_info = {
+ .copyright = "Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\n",
+ .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_VTY | L23_OPT_DBG,
+ .vty_info = &_modem_vty_info,
+ .vty_init = modem_vty_init,
+ .tun_data_ind_cb = modem_tun_data_ind_cb,
+};
diff --git a/src/host/layer23/src/modem/gmm.c b/src/host/layer23/src/modem/gmm.c
new file mode 100644
index 00000000..c0e69368
--- /dev/null
+++ b/src/host/layer23/src/modem/gmm.c
@@ -0,0 +1,271 @@
+/* GPRS GMM interfaces as per 3GPP TS 24.008, TS 24.007 */
+/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin@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/lienses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/tun.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+#include <osmocom/gprs/llc/llc.h>
+#include <osmocom/gprs/llc/llc_prim.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>
+#include <osmocom/gprs/gmm/gmm.h>
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+
+#include <osmocom/bb/common/settings.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/vty.h>
+#include <osmocom/bb/modem/gmm.h>
+#include <osmocom/bb/modem/sm.h>
+#include <osmocom/bb/modem/modem.h>
+
+static int modem_gmm_prim_up_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_gmm_prim_name(gmm_prim);
+ struct osmocom_ms *ms = user_data;
+ struct osmobb_apn *apn;
+ int rc = 0;
+
+ switch (gmm_prim->oph.sap) {
+ case OSMO_GPRS_GMM_SAP_GMMREG:
+ switch (OSMO_PRIM_HDR(&gmm_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_ATTACH, PRIM_OP_CONFIRM):
+ if (gmm_prim->gmmreg.attach_cnf.accepted) {
+ LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s: Attach success P-TMSI=0x%08x TLLI=0x%08x\n",
+ __func__, pdu_name, gmm_prim->gmmreg.attach_cnf.acc.allocated_ptmsi,
+ gmm_prim->gmmreg.attach_cnf.acc.allocated_tlli);
+ ms->subscr.gprs.ptmsi = gmm_prim->gmmreg.attach_cnf.acc.allocated_ptmsi;
+ ms->gmmlayer.tlli = gmm_prim->gmmreg.attach_cnf.acc.allocated_tlli;
+ app_data.modem_state = MODEM_ST_ATTACHED;
+ /* Activate APN if not yet already: */
+ llist_for_each_entry(apn, &ms->gprs.apn_list, list) {
+ if (apn->fsm.fi->state != APN_ST_INACTIVE)
+ continue;
+ osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GMM_ATTACHED, NULL);
+ modem_sm_smreg_pdp_act_req(ms, apn);
+ }
+ } else {
+ uint8_t cause = gmm_prim->gmmreg.attach_cnf.rej.cause;
+ LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s: Attach rejected, cause=%u (%s)\n",
+ __func__, pdu_name, cause, get_value_string(gsm48_gmm_cause_names, cause));
+ app_data.modem_state = MODEM_ST_IDLE;
+ modem_gprs_attach_if_needed(ms);
+ }
+ break;
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_SIM_AUTH, PRIM_OP_INDICATION):
+ LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s ac_ref_nr=%u key_seq=%u rand=%s\n",
+ __func__, pdu_name,
+ gmm_prim->gmmreg.sim_auth_ind.ac_ref_nr,
+ gmm_prim->gmmreg.sim_auth_ind.key_seq,
+ osmo_hexdump(gmm_prim->gmmreg.sim_auth_ind.rand,
+ sizeof(gmm_prim->gmmreg.sim_auth_ind.rand)));
+ /* Cache request information, it'll be needed during response time: */
+ ms->gmmlayer.ac_ref_nr = gmm_prim->gmmreg.sim_auth_ind.ac_ref_nr;
+ ms->gmmlayer.key_seq = gmm_prim->gmmreg.sim_auth_ind.key_seq;
+ memcpy(ms->gmmlayer.rand, gmm_prim->gmmreg.sim_auth_ind.rand,
+ sizeof(ms->gmmlayer.rand));
+ /* Request SIM to authenticate. Wait for signal S_L23_SUBSCR_SIM_AUTH_RESP. */
+ rc = gsm_subscr_generate_kc(ms, gmm_prim->gmmreg.sim_auth_ind.key_seq,
+ gmm_prim->gmmreg.sim_auth_ind.rand, false);
+ break;
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_CONFIRM):
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_INDICATION):
+ LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s\n", __func__, pdu_name);
+ ms_dispatch_all_apn(ms, APN_EV_GMM_DETACHED, NULL);
+ break;
+ default:
+ LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name);
+ break;
+ };
+ break;
+ case OSMO_GPRS_GMM_SAP_GMMSM:
+ switch (OSMO_PRIM_HDR(&gmm_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_CONFIRM):
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_RELEASE, PRIM_OP_INDICATION):
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_INDICATION):
+ case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_MODIFY, PRIM_OP_INDICATION):
+ osmo_gprs_sm_prim_gmm_lower_up(gmm_prim);
+ rc = 1; /* Tell RLCMAC that we take ownership of the prim. */
+ break;
+ default:
+ LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name);
+ break;
+ };
+ break;
+ default:
+ LOGP(DGMM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ return rc;
+}
+
+static int modem_gmm_prim_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_gmm_prim_name(gmm_prim);
+ int rc = 0;
+ uint32_t old_tlli, new_tlli;
+ struct osmocom_ms *ms, *ms_found = NULL;
+
+ osmo_static_assert(sizeof(struct osmo_gprs_gmm_gmmrr_prim) == sizeof(struct osmo_gprs_rlcmac_gmmrr_prim),
+ _gmmrr_prim_size);
+
+ switch (gmm_prim->oph.sap) {
+ case OSMO_GPRS_GMM_SAP_GMMRR:
+ OSMO_ASSERT(gmm_prim->oph.primitive == OSMO_GPRS_GMM_GMMRR_ASSIGN);
+ /* Update app TLLI reference. This usually happens as a result of a RAU ACCEPT */
+ old_tlli = gmm_prim->gmmrr.tlli;
+ new_tlli = gmm_prim->gmmrr.assign_req.new_tlli;
+ llist_for_each_entry(ms, &ms_list, entity) {
+ if (old_tlli != ms->gmmlayer.tlli)
+ continue;
+ ms_found = ms;
+ break;
+ }
+ if (ms_found) {
+ if (new_tlli != OSMO_GPRS_GMM_TLLI_UNASSIGNED) {
+ LOGP(DGMM, LOGL_INFO, "%s(): Rx %s Update TLLI 0x%08x -> 0x%08x\n",
+ __func__, pdu_name, old_tlli, new_tlli);
+ ms_found->gmmlayer.tlli = new_tlli;
+ } else {
+ LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s with TLLI=0x%08x is being released, GMM should be restarted?\n",
+ __func__, pdu_name, old_tlli);
+ }
+ } else {
+ if (old_tlli != OSMO_GPRS_GMM_TLLI_UNASSIGNED)
+ LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s with unknown TLLI=0x%08x, probably the MS is still attaching\n",
+ __func__, pdu_name, old_tlli);
+ }
+
+ /* Forward it to lower layers, pass ownership over to RLCMAC: */
+ /* Optimization: GMM-GMMRR-ASSIGN-REQ is 1-to-1 ABI compatible with
+ RLCMAC-GMMRR-ASSIGN-REQ, we just need to adapt the header.
+ See osmo_static_assert(_gmmrr_prim_size) above.
+ */
+ gmm_prim->oph.sap = OSMO_GPRS_RLCMAC_SAP_GMMRR;
+ gmm_prim->oph.primitive = OSMO_GPRS_RLCMAC_GMMRR_ASSIGN;
+ osmo_gprs_rlcmac_prim_upper_down((struct osmo_gprs_rlcmac_prim *)gmm_prim);
+ rc = 1; /* Tell GMM that we take ownership of the prim. */
+ break;
+ case OSMO_GPRS_GMM_SAP_GMMREG:
+ default:
+ LOGP(DGMM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ return rc;
+}
+
+static int modem_gmm_prim_llc_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
+{
+ int rc;
+
+ rc = osmo_gprs_llc_prim_upper_down(llc_prim);
+
+ /* LLC took ownership of the message, tell GMM layer to not free it: */
+ rc = 1;
+ return rc;
+}
+
+int modem_gmm_init(struct osmocom_ms *ms)
+{
+ int rc;
+ rc = osmo_gprs_gmm_init(OSMO_GPRS_GMM_LOCATION_MS);
+ if (rc != 0)
+ return rc;
+
+ osmo_gprs_gmm_set_log_cat(OSMO_GPRS_GMM_LOGC_GMM, DGMM);
+
+ osmo_gprs_gmm_prim_set_up_cb(modem_gmm_prim_up_cb, ms);
+ osmo_gprs_gmm_prim_set_down_cb(modem_gmm_prim_down_cb, ms);
+ osmo_gprs_gmm_prim_set_llc_down_cb(modem_gmm_prim_llc_down_cb, ms);
+
+ osmo_gprs_gmm_enable_gprs(true);
+ return rc;
+}
+
+int modem_gmm_gmmreg_attach_req(const struct osmocom_ms *ms)
+{
+ struct osmo_gprs_gmm_prim *gmm_prim;
+ const struct gsm_subscriber *subscr = &ms->subscr;
+ int rc;
+
+ gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_attach_req();
+ gmm_prim->gmmreg.attach_req.attach_type = OSMO_GPRS_GMM_ATTACH_TYPE_GPRS;
+ gmm_prim->gmmreg.attach_req.ptmsi = subscr->gprs.ptmsi;
+ gmm_prim->gmmreg.attach_req.ptmsi_sig = subscr->gprs.ptmsi_sig;
+ gmm_prim->gmmreg.attach_req.attach_with_imsi = (subscr->gprs.ptmsi == GSM_RESERVED_TMSI);
+ memcpy(gmm_prim->gmmreg.attach_req.imsi, subscr->imsi, ARRAY_SIZE(subscr->imsi));
+ memcpy(gmm_prim->gmmreg.attach_req.imei, ms->settings.imei, ARRAY_SIZE(ms->settings.imei));
+ memcpy(gmm_prim->gmmreg.attach_req.imeisv, ms->settings.imeisv, ARRAY_SIZE(ms->settings.imeisv));
+ memcpy(&gmm_prim->gmmreg.attach_req.old_rai, &subscr->gprs.rai, sizeof(subscr->gprs.rai));
+ rc = osmo_gprs_gmm_prim_upper_down(gmm_prim);
+ if (rc < 0)
+ LOGP(DMM, LOGL_ERROR, "Failed submitting GMMREG-ATTACH.req\n");
+ return rc;
+}
+
+int modem_gmm_gmmreg_detach_req(const struct osmocom_ms *ms)
+{
+ struct osmo_gprs_gmm_prim *gmm_prim;
+ const struct gsm_subscriber *subscr = &ms->subscr;
+ int rc;
+
+ gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_detach_req();
+ gmm_prim->gmmreg.detach_req.ptmsi = subscr->gprs.ptmsi;
+ gmm_prim->gmmreg.detach_req.detach_type = OSMO_GPRS_GMM_DETACH_MS_TYPE_GPRS;
+ gmm_prim->gmmreg.detach_req.poweroff_type = OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL;
+ rc = osmo_gprs_gmm_prim_upper_down(gmm_prim);
+ if (rc < 0)
+ LOGP(DMM, LOGL_ERROR, "Failed submitting GMMREG-DETACH.req\n");
+ return rc;
+}
+
+int modem_gmm_gmmreg_sim_auth_rsp(const struct osmocom_ms *ms, uint8_t *sres, uint8_t *kc, uint8_t kc_len)
+{
+ struct osmo_gprs_gmm_prim *gmm_prim;
+ int rc;
+
+ gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_sim_auth_rsp();
+ gmm_prim->gmmreg.sim_auth_rsp.ac_ref_nr = ms->gmmlayer.ac_ref_nr;
+ gmm_prim->gmmreg.sim_auth_rsp.key_seq = ms->gmmlayer.key_seq;
+ memcpy(gmm_prim->gmmreg.sim_auth_rsp.rand, ms->gmmlayer.rand,
+ sizeof(gmm_prim->gmmreg.sim_auth_rsp.rand));
+ memcpy(gmm_prim->gmmreg.sim_auth_rsp.sres, sres,
+ sizeof(gmm_prim->gmmreg.sim_auth_rsp.sres));
+ memcpy(gmm_prim->gmmreg.sim_auth_rsp.kc, kc,
+ kc_len);
+ rc = osmo_gprs_gmm_prim_upper_down(gmm_prim);
+ if (rc < 0)
+ LOGP(DMM, LOGL_ERROR, "Failed submitting GMMREG-SIM_AUTH.rsp\n");
+ return rc;
+}
diff --git a/src/host/layer23/src/modem/grr.c b/src/host/layer23/src/modem/grr.c
new file mode 100644
index 00000000..cd325c4c
--- /dev/null
+++ b/src/host/layer23/src/modem/grr.c
@@ -0,0 +1,875 @@
+/*
+ * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@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/lienses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/lapdm.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/sysinfo.h>
+#include <osmocom/bb/common/l1ctl.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/modem/modem.h>
+#include <osmocom/bb/modem/grr.h>
+
+#include <osmocom/bb/mobile/gsm322.h>
+#include <osmocom/bb/mobile/gsm48_rr.h>
+
+#include <l1ctl_proto.h>
+
+static uint32_t _gsm48_req_ref2fn(const struct gsm48_req_ref *ref)
+{
+ const struct gsm_time time = {
+ .t3 = ref->t3_high << 3 | ref->t3_low,
+ .t2 = ref->t2,
+ .t1 = ref->t1,
+ };
+ return gsm_gsmtime2fn(&time);
+}
+
+/* Generate an 8-bit CHANNEL REQUEST message as per 3GPP TS 44.018, 9.1.8 */
+static uint8_t grr_gen_chan_req(bool single_block)
+{
+ uint8_t rnd = (uint8_t)rand();
+
+ if (single_block) /* 01110xxx */
+ return 0x70 | (rnd & 0x07);
+
+ /* 011110xx or 01111x0x or 01111xx0 */
+ if ((rnd & 0x07) == 0x07)
+ return 0x78;
+ return 0x78 | (rnd & 0x07);
+}
+
+static bool grr_match_req_ref(struct osmocom_ms *ms,
+ const struct gsm48_req_ref *ref)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(rr->cr_hist); i++) {
+ const struct gsm48_cr_hist *hist = &rr->cr_hist[i];
+ if (!hist->valid)
+ continue;
+ if (memcmp(&hist->ref, ref, sizeof(*ref)) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static int forward_to_rlcmac(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct osmo_gprs_rlcmac_prim *rlcmac_prim;
+ const uint32_t fn = *(uint32_t *)(&msg->cb[0]);
+
+ /* Forward a CCCH/BCCH block to the RLC/MAC layer */
+ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(fn, msgb_l3(msg));
+ return osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim);
+}
+
+static int grr_handle_si1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si1_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo1(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to decode SI1 message\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int grr_handle_si3(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si3_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo3(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to decode SI3 message\n");
+ return rc;
+ }
+
+ if (cs->ccch_mode == CCCH_MODE_NONE) {
+ if (cs->sel_si.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ cs->ccch_mode = CCCH_MODE_COMBINED;
+ else
+ cs->ccch_mode = CCCH_MODE_NON_COMBINED;
+ l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
+ }
+
+ if (!cs->sel_si.gprs.supported) {
+ LOGP(DRR, LOGL_NOTICE, "SI3 Rest Octets IE contains no GPRS Indicator\n");
+ return 0;
+ }
+
+ LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n",
+ cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm");
+
+ return 0;
+}
+
+static int grr_handle_si4(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si4_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo4(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0) {
+ LOGP(DRR, LOGL_ERROR, "Failed to decode SI4 message\n");
+ return rc;
+ }
+
+ if (!cs->sel_si.gprs.supported) {
+ LOGP(DRR, LOGL_NOTICE, "SI4 Rest Octets IE contains no GPRS Indicator\n");
+ return 0;
+ }
+
+ LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n",
+ cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm");
+
+ return 0;
+}
+
+static int grr_handle_si13(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int rc;
+
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN)
+ return -EINVAL;
+ if (!memcmp(&cs->sel_si.si13_msg[0], msgb_l3(msg), msgb_l3len(msg)))
+ return 0; /* this message is already handled */
+
+ rc = gsm48_decode_sysinfo13(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg));
+ if (rc != 0)
+ return rc;
+
+ /* Forward SI13 to RLC/MAC layer */
+ return forward_to_rlcmac(ms, msg);
+}
+
+static int grr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_system_information_type_header *si_hdr = msgb_l3(msg);
+ const uint8_t si_type = si_hdr->system_information;
+ const uint32_t fn = *(uint32_t *)(&msg->cb[0]);
+
+ LOGP(DRR, LOGL_INFO, "BCCH message (type=0x%02x, fn=%u): %s\n",
+ si_type, fn, gsm48_rr_msg_name(si_type));
+
+ switch (si_type) {
+ case GSM48_MT_RR_SYSINFO_1:
+ return grr_handle_si1(ms, msg);
+ case GSM48_MT_RR_SYSINFO_3:
+ return grr_handle_si3(ms, msg);
+ case GSM48_MT_RR_SYSINFO_4:
+ return grr_handle_si4(ms, msg);
+ case GSM48_MT_RR_SYSINFO_13:
+ return grr_handle_si13(ms, msg);
+ default:
+ return 0;
+ };
+}
+
+static int grr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_imm_ass *ia = msgb_l3(msg);
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ /* 3GPP TS 44.018, section 10.5.2.25b "Dedicated mode or TBF".
+ * As per table 9.1.18.1, only the value part (4 bits) is present in the
+ * IMMEDIATE ASSIGNMENT message. In struct gsm48_imm_ass it's combined
+ * with the Page Mode IE, perhaps due to historical reasons. */
+ const uint8_t dm_or_tbf = ia->page_mode >> 4;
+
+ /* T/D flag: discard dedicated channel assignment */
+ if ((dm_or_tbf & (1 << 0)) == 0) {
+ LOGP(DRR, LOGL_INFO,
+ "%s(): Discarding IMM ASS: dedicated channel assignment\n",
+ __func__);
+ return 0;
+ }
+ /* NRA flag: discard No Resource Allocated */
+ if ((dm_or_tbf & (1 << 3)) != 0) {
+ LOGP(DRR, LOGL_INFO,
+ "%s(): Discarding IMM ASS: NRA flag is set\n",
+ __func__);
+ return 0;
+ }
+
+ /* If this is an Uplink TBF assignment, check the Request Reference IE.
+ * Checking this IE in Downlink TBF assignment makes no sense because
+ * no CHANNEL REQUEST was sent by the MS prior to it. */
+ if ((dm_or_tbf & (1 << 1)) == 0) {
+ if (rr->state != GSM48_RR_ST_CONN_PEND) {
+ LOGP(DRR, LOGL_INFO,
+ "%s(): rr_state != GSM48_RR_ST_CONN_PEND\n", __func__);
+ return 0;
+ }
+ if (!grr_match_req_ref(ms, &ia->req_ref)) {
+ LOGP(DRR, LOGL_INFO,
+ "%s(): req_ref mismatch (RA=0x%02x, T1=%u, T3=%u, T2=%u, FN=%u)\n",
+ __func__, ia->req_ref.ra, ia->req_ref.t1,
+ ia->req_ref.t3_high << 3 | ia->req_ref.t3_low, ia->req_ref.t2,
+ _gsm48_req_ref2fn(&ia->req_ref));
+ return 0;
+ }
+ }
+
+ return forward_to_rlcmac(ms, msg);
+}
+
+/* TS 44.018 9.1.22 "Paging request type 1" */
+static int grr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ LOGP(DRR, LOGL_INFO, "Rx Paging Request Type 1\n");
+
+ return forward_to_rlcmac(ms, msg);
+}
+
+/* TS 44.018 9.1.23 "Paging request type 2" */
+static int grr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
+{
+ LOGP(DRR, LOGL_INFO, "Rx Paging Request Type 2\n");
+
+ return forward_to_rlcmac(ms, msg);
+}
+
+/* 9.1.24 Paging request type 3 */
+static int grr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
+{
+ LOGP(DRR, LOGL_INFO, "Rx Paging Request Type 3\n");
+
+ /* Paging Request Type 3 contains 4 TMSI/P-TMSI, but P3 Rest Octets
+ contain no "Packet Page Indication" IE, hence it cannot be used to page
+ for GPRS. Simply ignore it. */
+ return 0;
+}
+
+/* Dummy Paging Request 1 with "no identity" */
+static const uint8_t paging_fill[] = {
+ 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b,
+ /* The rest part may be randomized */
+};
+
+/* LAPDm func=UI fill frame (for the BTS side) */
+static const uint8_t lapdm_fill[] = {
+ 0x03, 0x03, 0x01, 0x2b,
+ /* The rest part may be randomized */
+};
+
+/* TODO: share / generalize this code */
+static bool is_fill_frame(const struct msgb *msg)
+{
+ const uint8_t *l2 = msgb_l3(msg);
+
+ if (!memcmp(l2, paging_fill, sizeof(paging_fill)))
+ return true;
+ if (!memcmp(l2, lapdm_fill, sizeof(lapdm_fill)))
+ return true;
+
+ return false;
+}
+
+static int grr_rx_ccch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+
+ /* Skip frames with wrong length */
+ if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) {
+ LOGP(DRR, LOGL_ERROR, "Rx CCCH message with odd length=%u: %s\n",
+ msgb_l3len(msg), msgb_hexdump_l3(msg));
+ return -EINVAL;
+ }
+
+ /* Skip dummy (fill) frames */
+ if (is_fill_frame(msg))
+ return 0;
+
+ if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) {
+ LOGP(DRR, LOGL_ERROR, "PCH pdisc (%s) != RR\n",
+ gsm48_pdisc_name(sih->rr_protocol_discriminator));
+ }
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_IMM_ASS:
+ return grr_rx_imm_ass(ms, msg);
+ case GSM48_MT_RR_PAG_REQ_1:
+ return grr_rx_pag_req_1(ms, msg);
+ case GSM48_MT_RR_PAG_REQ_2:
+ return grr_rx_pag_req_2(ms, msg);
+ case GSM48_MT_RR_PAG_REQ_3:
+ return grr_rx_pag_req_3(ms, msg);
+ default:
+ return 0;
+ }
+}
+
+static int grr_rx_rslms_rll_ud(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct tlv_parsed tv;
+
+ DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
+ rllh->chan_nr, rllh->link_id);
+
+ if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) {
+ LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DRSL, LOGL_ERROR, "UNIT_DATA_IND without L3 INFO ?!?\n");
+ return -EINVAL;
+ }
+
+ msg->l3h = (uint8_t *)TLVP_VAL(&tv, RSL_IE_L3_INFO);
+
+ switch (rllh->chan_nr) {
+ case RSL_CHAN_PCH_AGCH:
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PCH_AGCH_BLOCK_IND, msg);
+ case RSL_CHAN_BCCH:
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_BCCH_BLOCK_IND, msg);
+ default:
+ return 0;
+ }
+}
+
+static int grr_rx_rslms_rll(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+
+ switch (rllh->c.msg_type) {
+ case RSL_MT_UNIT_DATA_IND:
+ return grr_rx_rslms_rll_ud(ms, msg);
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms RLL message "
+ "(msg_type 0x%02x)\n", rllh->c.msg_type);
+ return -EINVAL;
+ }
+}
+
+static int grr_rx_rslms_cchan(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct abis_rsl_cchan_hdr *ch = msgb_l2(msg);
+
+ switch (ch->c.msg_type) {
+ case RSL_MT_CHAN_CONF: /* RACH.conf */
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_CHAN_ACCESS_CNF,
+ (void *)&ch->data[1]);
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms CCHAN message "
+ "(msg_type 0x%02x)\n", ch->c.msg_type);
+ return -EINVAL;
+ }
+}
+
+int modem_grr_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
+{
+ const struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc;
+
+ /* Obtain FN from message context: */
+ *(uint32_t *)(&msg->cb[0]) = le->datalink[DL_SAPI0].mctx.fn;
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = grr_rx_rslms_rll((struct osmocom_ms *)ctx, msg);
+ break;
+ case ABIS_RSL_MDISC_COM_CHAN:
+ rc = grr_rx_rslms_cchan((struct osmocom_ms *)ctx, msg);
+ break;
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms message "
+ "(msg_discr 0x%02x)\n", rslh->msg_discr);
+ rc = -EINVAL;
+ break;
+ }
+
+ msgb_free(msg);
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define S(x) (1 << (x))
+
+#include <osmocom/gsm/gsm0502.h> // XXX
+
+/* RACH re-transmission delay value (in ms) */
+#define GRR_PACKET_ACCESS_DELAY_MS 300
+/* RACH max number of transmissions */
+#define GRR_PACKET_ACCESS_MAX_CHAN_REQ 3
+
+static void handle_chan_access_req(struct osmo_fsm_inst *fi,
+ const struct osmo_gprs_rlcmac_l1ctl_prim *lp)
+{
+ struct osmocom_ms *ms = fi->priv;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ if (lp->rach_req.is_11bit) { /* TODO: implement 11-bit RACH */
+ LOGPFSML(fi, LOGL_ERROR, "11-bit RACH is not supported\n");
+ return;
+ }
+
+ memset(&rr->cr_hist[0], 0x00, sizeof(rr->cr_hist));
+ rr->chan_req_val = lp->rach_req.ra & ~0x07;
+ rr->n_chan_req = GRR_PACKET_ACCESS_MAX_CHAN_REQ;
+ rr->state = GSM48_RR_ST_CONN_PEND;
+
+ osmo_fsm_inst_state_chg_ms(fi, GRR_ST_PACKET_ACCESS,
+ GRR_PACKET_ACCESS_DELAY_MS, 0);
+}
+
+static void handle_pdch_establish_req(struct osmo_fsm_inst *fi,
+ const struct osmo_gprs_rlcmac_l1ctl_prim *lp)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ if (!lp->pdch_est_req.fh) {
+ LOGPFSML(fi, LOGL_INFO,
+ "PDCH Establish.Req: TSC=%u, H0, ARFCN=%u\n",
+ lp->pdch_est_req.tsc, lp->pdch_est_req.arfcn);
+ l1ctl_tx_dm_est_req_h0(ms, lp->pdch_est_req.arfcn,
+ RSL_CHAN_OSMO_PDCH | lp->pdch_est_req.ts_nr,
+ lp->pdch_est_req.tsc, GSM48_CMODE_SIGN, 0, 0);
+ } else {
+ /* Hopping */
+ uint8_t ma_len = 0;
+ uint16_t ma[64];
+
+ LOGPFSML(fi, LOGL_INFO,
+ "PDCH Establish.Req: TSC=%u, H1, HSN=%u, MAIO=%u\n",
+ lp->pdch_est_req.tsc,
+ lp->pdch_est_req.fhp.hsn,
+ lp->pdch_est_req.fhp.maio);
+
+ for (unsigned int i = 1, j = 0; i <= 1024; i++) {
+ unsigned int arfcn = i & 1023;
+ unsigned int k;
+
+ if (~ms->cellsel.sel_si.freq[arfcn].mask & 0x01)
+ continue;
+
+ k = lp->pdch_est_req.fhp.ma_len - (j >> 3) - 1;
+ if (lp->pdch_est_req.fhp.ma[k] & (1 << (j & 7)))
+ ma[ma_len++] = arfcn;
+ j++;
+ }
+
+ l1ctl_tx_dm_est_req_h1(ms,
+ lp->pdch_est_req.fhp.maio,
+ lp->pdch_est_req.fhp.hsn,
+ &ma[0], ma_len,
+ RSL_CHAN_OSMO_PDCH | lp->pdch_est_req.ts_nr,
+ lp->pdch_est_req.tsc, GSM48_CMODE_SIGN, 0, 0);
+ }
+
+ osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_TRANSFER, 0, 0);
+}
+
+static void handle_pdch_block_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_gprs_ul_block_cnf *cnf = (void *)msg->l1h;
+ const uint32_t fn = osmo_load32be(&cnf->fn);
+ struct osmo_gprs_rlcmac_prim *prim;
+
+ prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_cnf(cnf->tn, fn,
+ msgb_l2(msg),
+ msgb_l2len(msg));
+ osmo_gprs_rlcmac_prim_lower_up(prim);
+}
+
+static void handle_pdch_block_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_gprs_dl_block_ind *ind = (void *)msg->l1h;
+ const uint32_t fn = osmo_load32be(&ind->hdr.fn);
+ struct osmo_gprs_rlcmac_prim *prim;
+
+ /* FIXME: sadly, rlcmac_prim_l1ctl_alloc() is not exposed */
+ prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(0, 0, 0, 0, 0, NULL, 0);
+ prim->l1ctl = (struct osmo_gprs_rlcmac_l1ctl_prim) {
+ .pdch_data_ind = {
+ .fn = fn,
+ .ts_nr = ind->hdr.tn,
+ .rx_lev = ind->meas.rx_lev,
+ .ber10k = osmo_load16be(&ind->meas.ber10k),
+ .ci_cb = osmo_load16be(&ind->meas.ci_cb),
+ .data_len = msgb_l2len(msg),
+ .data = msgb_l2(msg),
+ }
+ };
+ osmo_gprs_rlcmac_prim_lower_up(prim);
+}
+
+static void handle_pdch_rts_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_gprs_rts_ind *ind = (void *)msg->l1h;
+ const uint32_t fn = osmo_load32be(&ind->fn);
+ struct osmo_gprs_rlcmac_prim *prim;
+
+ prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ind->tn, fn, ind->usf);
+ osmo_gprs_rlcmac_prim_lower_up(prim);
+}
+
+static bool grr_cell_is_usable(const struct osmocom_ms *ms)
+{
+ const struct gsm322_cellsel *cs = &ms->cellsel;
+ const struct gsm48_sysinfo *si = &cs->sel_si;
+
+ if (cs->sync_pending) /* FBSB in process */
+ return false;
+
+ if (!si->si1 || !si->si3 || !si->si4 || !si->si13)
+ return false;
+ if (!si->gprs.supported)
+ return false;
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void grr_st_packet_not_ready_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ switch (event) {
+ case GRR_EV_BCCH_BLOCK_IND:
+ grr_rx_bcch(ms, (struct msgb *)data);
+ if (grr_cell_is_usable(ms)) {
+ LOGPFSML(fi, LOGL_NOTICE, "Cell is usable, GRR becomes ready\n");
+ osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_IDLE, 0, 0);
+ }
+ break;
+ case GRR_EV_PCH_AGCH_BLOCK_IND:
+ grr_rx_ccch(ms, (struct msgb *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void grr_st_packet_idle_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = fi->priv;
+ struct osmo_gprs_rlcmac_prim *prim;
+
+ prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_ready_ind();
+ osmo_gprs_rlcmac_prim_lower_up(prim);
+
+ modem_gprs_attach_if_needed(ms);
+}
+
+static void grr_st_packet_idle_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ switch (event) {
+ case GRR_EV_BCCH_BLOCK_IND:
+ grr_rx_bcch(ms, (struct msgb *)data);
+ if (!grr_cell_is_usable(ms)) {
+ LOGPFSML(fi, LOGL_NOTICE, "Cell is not usable, GRR becomes not ready\n");
+ osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0);
+ }
+ break;
+ case GRR_EV_PCH_AGCH_BLOCK_IND:
+ grr_rx_ccch(ms, (struct msgb *)data);
+ break;
+ case GRR_EV_CHAN_ACCESS_REQ:
+ handle_chan_access_req(fi, data);
+ break;
+ case GRR_EV_PDCH_ESTABLISH_REQ:
+ handle_pdch_establish_req(fi, data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void grr_st_packet_access_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = fi->priv;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ if (rr->n_chan_req == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Packet access failed (no more attempts left)\n");
+ osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0);
+ return;
+ }
+
+ /* (re-)generate the RA value */
+ if ((rr->chan_req_val >> 3) == 0x0e) /* 01110xxx */
+ rr->cr_ra = grr_gen_chan_req(true);
+ else /* 011110xx or 01111x0x or 01111xx0 */
+ rr->cr_ra = grr_gen_chan_req(false);
+ rr->n_chan_req--;
+
+ LOGPFSML(fi, LOGL_INFO,
+ "Sending CHANNEL REQUEST (ra=0x%02x, num_attempts=%u)\n",
+ rr->cr_ra, rr->n_chan_req);
+
+ l1ctl_tx_rach_req(ms, RSL_CHAN_RACH, 0x00, rr->cr_ra, 0,
+ ms->cellsel.ccch_mode == CCCH_MODE_COMBINED, 0xff);
+}
+
+static void grr_st_packet_access_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ switch (event) {
+ case GRR_EV_BCCH_BLOCK_IND:
+ grr_rx_bcch(ms, (struct msgb *)data);
+ if (!grr_cell_is_usable(ms)) {
+ LOGPFSML(fi, LOGL_NOTICE, "Cell is not usable, GRR becomes not ready\n");
+ osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0);
+ }
+ break;
+ case GRR_EV_PCH_AGCH_BLOCK_IND:
+ grr_rx_ccch(ms, (struct msgb *)data);
+ break;
+ case GRR_EV_CHAN_ACCESS_CNF:
+ {
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ const struct gsm48_req_ref *ref = data;
+
+ LOGPFSML(fi, LOGL_NOTICE,
+ "Rx RACH.conf (RA=0x%02x, T1=%u, T3=%u, T2=%u, FN=%u)\n",
+ rr->cr_ra, ref->t1, ref->t3_high << 3 | ref->t3_low, ref->t2,
+ _gsm48_req_ref2fn(ref));
+
+ if (ms->rrlayer.state != GSM48_RR_ST_CONN_PEND) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx unexpected RACH.conf\n");
+ return;
+ }
+
+ /* shift the CHANNEL REQUEST history buffer */
+ memmove(&rr->cr_hist[1], &rr->cr_hist[0],
+ sizeof(rr->cr_hist) - sizeof(rr->cr_hist[0]));
+ /* store the new entry */
+ rr->cr_hist[0].ref = *ref;
+ rr->cr_hist[0].ref.ra = rr->cr_ra;
+ rr->cr_hist[0].valid = 1;
+ break;
+ }
+ case GRR_EV_PDCH_ESTABLISH_REQ:
+ handle_pdch_establish_req(fi, data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void grr_st_packet_transfer_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ ms->rrlayer.state = GSM48_RR_ST_DEDICATED;
+}
+
+static void grr_st_packet_transfer_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ ms->rrlayer.state = GSM48_RR_ST_IDLE;
+}
+
+static void grr_st_packet_transfer_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct osmocom_ms *ms = fi->priv;
+
+ switch (event) {
+ case GRR_EV_PDCH_UL_TBF_CFG_REQ:
+ {
+ const struct osmo_gprs_rlcmac_l1ctl_prim *lp = data;
+ l1ctl_tx_gprs_ul_tbf_cfg_req(ms,
+ lp->cfg_ul_tbf_req.ul_tbf_nr,
+ lp->cfg_ul_tbf_req.ul_slotmask,
+ lp->cfg_ul_tbf_req.start_fn);
+ break;
+ }
+ case GRR_EV_PDCH_DL_TBF_CFG_REQ:
+ {
+ const struct osmo_gprs_rlcmac_l1ctl_prim *lp = data;
+ l1ctl_tx_gprs_dl_tbf_cfg_req(ms,
+ lp->cfg_dl_tbf_req.dl_tbf_nr,
+ lp->cfg_dl_tbf_req.dl_slotmask,
+ lp->cfg_ul_tbf_req.start_fn,
+ lp->cfg_dl_tbf_req.dl_tfi);
+ break;
+ }
+ case GRR_EV_PDCH_BLOCK_REQ:
+ {
+ const struct osmo_gprs_rlcmac_l1ctl_prim *lp = data;
+ l1ctl_tx_gprs_ul_block_req(ms,
+ lp->pdch_data_req.fn,
+ lp->pdch_data_req.ts_nr,
+ lp->pdch_data_req.data,
+ lp->pdch_data_req.data_len);
+ break;
+ }
+ case GRR_EV_PDCH_BLOCK_CNF:
+ handle_pdch_block_cnf(ms, (struct msgb *)data);
+ break;
+ case GRR_EV_PDCH_BLOCK_IND:
+ handle_pdch_block_ind(ms, (struct msgb *)data);
+ break;
+ case GRR_EV_PDCH_RTS_IND:
+ handle_pdch_rts_ind(ms, (struct msgb *)data);
+ break;
+ case GRR_EV_PDCH_RELEASE_REQ:
+ modem_sync_to_cell(ms);
+ osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int grr_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ switch (fi->state) {
+ case GRR_ST_PACKET_ACCESS:
+ /* perform a loop transaction, restarting the timer */
+ osmo_fsm_inst_state_chg_ms(fi, GRR_ST_PACKET_ACCESS,
+ GRR_PACKET_ACCESS_DELAY_MS, 0);
+ return 0;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static const struct osmo_fsm_state grr_fsm_states[] = {
+ [GRR_ST_PACKET_NOT_READY] = {
+ .name = "PACKET_NOT_READY",
+ .out_state_mask = S(GRR_ST_PACKET_IDLE),
+ .in_event_mask = S(GRR_EV_BCCH_BLOCK_IND)
+ | S(GRR_EV_PCH_AGCH_BLOCK_IND),
+ .action = &grr_st_packet_not_ready_action,
+ },
+ [GRR_ST_PACKET_IDLE] = {
+ .name = "PACKET_IDLE",
+ .out_state_mask = S(GRR_ST_PACKET_NOT_READY)
+ | S(GRR_ST_PACKET_ACCESS)
+ | S(GRR_ST_PACKET_TRANSFER),
+ .in_event_mask = S(GRR_EV_BCCH_BLOCK_IND)
+ | S(GRR_EV_PCH_AGCH_BLOCK_IND)
+ | S(GRR_EV_CHAN_ACCESS_REQ)
+ | S(GRR_EV_PDCH_ESTABLISH_REQ), /* DL TBF ASS */
+ .action = &grr_st_packet_idle_action,
+ .onenter = &grr_st_packet_idle_onenter,
+ },
+ [GRR_ST_PACKET_ACCESS] = {
+ .name = "PACKET_ACCESS",
+ .out_state_mask = S(GRR_ST_PACKET_NOT_READY)
+ | S(GRR_ST_PACKET_ACCESS)
+ | S(GRR_ST_PACKET_TRANSFER),
+ .in_event_mask = S(GRR_EV_BCCH_BLOCK_IND)
+ | S(GRR_EV_PCH_AGCH_BLOCK_IND)
+ | S(GRR_EV_CHAN_ACCESS_CNF)
+ | S(GRR_EV_PDCH_ESTABLISH_REQ), /* UL TBF ASS */
+ .onenter = &grr_st_packet_access_onenter,
+ .action = &grr_st_packet_access_action,
+ },
+ [GRR_ST_PACKET_TRANSFER] = {
+ .name = "PACKET_TRANSFER",
+ .out_state_mask = S(GRR_ST_PACKET_NOT_READY),
+ .in_event_mask = S(GRR_EV_PDCH_UL_TBF_CFG_REQ)
+ | S(GRR_EV_PDCH_DL_TBF_CFG_REQ)
+ | S(GRR_EV_PDCH_BLOCK_REQ)
+ | S(GRR_EV_PDCH_BLOCK_CNF)
+ | S(GRR_EV_PDCH_BLOCK_IND)
+ | S(GRR_EV_PDCH_RTS_IND)
+ | S(GRR_EV_PDCH_RELEASE_REQ),
+ .action = &grr_st_packet_transfer_action,
+ .onenter = &grr_st_packet_transfer_onenter,
+ .onleave = &grr_st_packet_transfer_onleave,
+ },
+};
+
+static const struct value_string grr_fsm_event_names[] = {
+ OSMO_VALUE_STRING(GRR_EV_BCCH_BLOCK_IND),
+ OSMO_VALUE_STRING(GRR_EV_PCH_AGCH_BLOCK_IND),
+ OSMO_VALUE_STRING(GRR_EV_CHAN_ACCESS_REQ),
+ OSMO_VALUE_STRING(GRR_EV_CHAN_ACCESS_CNF),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_ESTABLISH_REQ),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_RELEASE_REQ),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_UL_TBF_CFG_REQ),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_DL_TBF_CFG_REQ),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_BLOCK_REQ),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_BLOCK_CNF),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_BLOCK_IND),
+ OSMO_VALUE_STRING(GRR_EV_PDCH_RTS_IND),
+ { 0, NULL }
+};
+
+struct osmo_fsm grr_fsm_def = {
+ .name = "GPRS-RR",
+ .log_subsys = DRR,
+ .states = grr_fsm_states,
+ .num_states = ARRAY_SIZE(grr_fsm_states),
+ .event_names = grr_fsm_event_names,
+ .timer_cb = &grr_fsm_timer_cb,
+};
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&grr_fsm_def) == 0);
+}
diff --git a/src/host/layer23/src/modem/llc.c b/src/host/layer23/src/modem/llc.c
new file mode 100644
index 00000000..fe920ee7
--- /dev/null
+++ b/src/host/layer23/src/modem/llc.c
@@ -0,0 +1,181 @@
+/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
+/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin@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/lienses/>.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/prim.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/llc/llc_prim.h>
+#include <osmocom/gprs/llc/llc.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+#include <osmocom/gprs/sndcp/sndcp_prim.h>
+
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/modem/llc.h>
+
+static int modem_llc_handle_ll_gmm(struct osmo_gprs_llc_prim *llc_prim)
+{
+ int rc;
+
+ switch (llc_prim->oph.primitive) {
+ case OSMO_GPRS_LLC_LL_UNITDATA:
+ break;
+ case OSMO_GPRS_LLC_LL_RESET:
+ case OSMO_GPRS_LLC_LL_ESTABLISH:
+ case OSMO_GPRS_LLC_LL_XID:
+ case OSMO_GPRS_LLC_LL_DATA:
+ case OSMO_GPRS_LLC_LL_STATUS:
+ default:
+ LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx LL prim %u\n",
+ __func__, llc_prim->oph.primitive);
+ return -EINVAL;
+ }
+
+ /* GMM took ownership of the message, tell LLC layer to not free it: */
+ rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim);
+ rc = 1;
+ return rc;
+}
+
+static int modem_llc_handle_ll_sndcp(struct osmo_gprs_llc_prim *llc_prim)
+{
+ int rc;
+ switch (llc_prim->oph.primitive) {
+ case OSMO_GPRS_LLC_LL_RESET:
+ case OSMO_GPRS_LLC_LL_ESTABLISH:
+ case OSMO_GPRS_LLC_LL_XID:
+ case OSMO_GPRS_LLC_LL_DATA:
+ case OSMO_GPRS_LLC_LL_UNITDATA:
+ case OSMO_GPRS_LLC_LL_STATUS:
+ case OSMO_GPRS_LLC_LL_ASSIGN:
+ /* Forward it to upper layers, pass owneserip over to SNDCP: */
+ osmo_gprs_sndcp_prim_lower_up(llc_prim);
+ rc = 1; /* Tell LLC that we take ownership of the prim. */
+ break;
+ default:
+ LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx LL prim %u\n",
+ __func__, llc_prim->oph.primitive);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+int modem_llc_prim_up_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim);
+ int rc = 0;
+
+ switch (llc_prim->oph.sap) {
+ case OSMO_GPRS_LLC_SAP_LLGMM:
+ LOGP(DLLC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x\n",
+ __func__, pdu_name, llc_prim->llgmm.tlli);
+ break;
+ case OSMO_GPRS_LLC_SAP_LL:
+ LOGP(DLLC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x SAPI=%s l3=[%s]\n",
+ __func__, pdu_name, llc_prim->ll.tlli,
+ osmo_gprs_llc_sapi_name(llc_prim->ll.sapi),
+ osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len));
+
+ switch (llc_prim->ll.sapi) {
+ case OSMO_GPRS_LLC_SAPI_GMM:
+ rc = modem_llc_handle_ll_gmm(llc_prim);
+ break;
+ case OSMO_GPRS_LLC_SAPI_SNDCP3:
+ case OSMO_GPRS_LLC_SAPI_SNDCP5:
+ case OSMO_GPRS_LLC_SAPI_SNDCP9:
+ case OSMO_GPRS_LLC_SAPI_SNDCP11:
+ rc = modem_llc_handle_ll_sndcp(llc_prim);
+ break;
+ case OSMO_GPRS_LLC_SAPI_TOM2:
+ case OSMO_GPRS_LLC_SAPI_SMS:
+ case OSMO_GPRS_LLC_SAPI_TOM8:
+ LOGP(DLLC, LOGL_NOTICE, "%s(): Unimplemented Rx llc_sapi %s\n", __func__, pdu_name);
+ rc = -EINVAL;
+ break;
+ default:
+ LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx llc_sapi %s\n", __func__, pdu_name);
+ rc = -EINVAL;
+ break;
+ }
+ break;
+ default:
+ LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+ return rc;
+}
+
+int modem_llc_prim_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim);
+ int rc = 0;
+
+ osmo_static_assert(sizeof(struct osmo_gprs_llc_grr_prim) == sizeof(struct osmo_gprs_rlcmac_grr_prim),
+ _grr_prim_size);
+
+ switch (llc_prim->oph.sap) {
+ case OSMO_GPRS_LLC_SAP_GRR:
+ LOGP(DLLC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x SAPI=%s RadioPrio=%u ll=[%s]\n",
+ __func__, pdu_name, llc_prim->grr.tlli,
+ /* data_req.sapi in same union pos: */
+ osmo_gprs_llc_sapi_name(llc_prim->grr.unitdata_req.sapi),
+ /* data_req.radio_prio in same union pos: */
+ llc_prim->grr.unitdata_req.radio_prio,
+ osmo_hexdump(llc_prim->grr.ll_pdu, llc_prim->grr.ll_pdu_len));
+ /* Forward it to lower layers, pass ownership over to RLCMAC: */
+ /* Optimization: LLC-GRR-UNITDATA-IND is 1-to-1 ABI compatible with
+ RLCMAC-GRR-UNITDATA-IND, we just need to adapt the header.
+ See osmo_static_assert(_grr_prim_size) above.
+ */
+ llc_prim->oph.sap = OSMO_GPRS_RLCMAC_SAP_GRR;
+ llc_prim->oph.primitive = OSMO_GPRS_RLCMAC_GRR_UNITDATA;
+ osmo_gprs_rlcmac_prim_upper_down((struct osmo_gprs_rlcmac_prim *)llc_prim);
+ rc = 1; /* Tell RLCMAC that we take ownership of the prim. */
+ break;
+ default:
+ LOGP(DLLC, LOGL_DEBUG, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+ return rc;
+}
+
+int modem_llc_init(struct osmocom_ms *ms, const char *cipher_plugin_path)
+{
+ int rc;
+ rc = osmo_gprs_llc_init(OSMO_GPRS_LLC_LOCATION_MS, cipher_plugin_path);
+ if (rc != 0)
+ return rc;
+
+ osmo_gprs_llc_set_log_cat(OSMO_GPRS_LLC_LOGC_LLC, DLLC);
+
+ osmo_gprs_llc_prim_set_up_cb(modem_llc_prim_up_cb, ms);
+ osmo_gprs_llc_prim_set_down_cb(modem_llc_prim_down_cb, ms);
+ return rc;
+}
diff --git a/src/host/layer23/src/modem/rlcmac.c b/src/host/layer23/src/modem/rlcmac.c
new file mode 100644
index 00000000..7f7b5661
--- /dev/null
+++ b/src/host/layer23/src/modem/rlcmac.c
@@ -0,0 +1,199 @@
+/* GPRS RLC/MAC protocol implementation as per 3GPP TS 44.060 */
+/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin@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/lienses/>.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+#include <osmocom/gprs/rlcmac/rlcmac.h>
+#include <osmocom/gprs/llc/llc_prim.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/modem/rlcmac.h>
+#include <osmocom/bb/modem/grr.h>
+
+static int modem_rlcmac_handle_grr(struct osmo_gprs_rlcmac_prim *rlcmac_prim)
+{
+ int rc;
+
+ osmo_static_assert(sizeof(struct osmo_gprs_rlcmac_grr_prim) == sizeof(struct osmo_gprs_llc_grr_prim),
+ _grr_prim_size);
+
+ switch (rlcmac_prim->oph.primitive) {
+ case OSMO_GPRS_RLCMAC_GRR_UNITDATA:
+ /* Forward it to upper layers, pass ownership over to LLC: */
+ /* Optimization: RLCMAC-GRR-UNITDATA-IND is 1-to-1 ABI compatible with
+ LLC-GRR-UNITDATA-IND, we just need to adapt the header.
+ See osmo_static_assert(_grr_prim_size) above.
+ */
+ rlcmac_prim->oph.sap = OSMO_GPRS_LLC_SAP_GRR;
+ rlcmac_prim->oph.primitive = OSMO_GPRS_LLC_GRR_UNITDATA;
+ osmo_gprs_llc_prim_lower_up((struct osmo_gprs_llc_prim *)rlcmac_prim);
+ rc = 1; /* Tell RLCMAC that we take ownership of the prim. */
+ break;
+ case OSMO_GPRS_RLCMAC_GRR_DATA:
+ default:
+ LOGP(DRLCMAC, LOGL_NOTICE, "%s(): Unexpected Rx RLCMAC GRR prim %u\n",
+ __func__, rlcmac_prim->oph.primitive);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int modem_rlcmac_handle_gmmrr(struct osmo_gprs_rlcmac_prim *rlcmac_prim)
+{
+ struct osmo_gprs_gmm_prim *gmm_prim;
+ int rc;
+
+ osmo_static_assert(sizeof(struct osmo_gprs_rlcmac_gmmrr_prim) == sizeof(struct osmo_gprs_gmm_gmmrr_prim),
+ _gmmrr_prim_size);
+
+ switch (rlcmac_prim->oph.primitive) {
+ case OSMO_GPRS_RLCMAC_GMMRR_PAGE:
+ /* Forward it to upper layers, pass ownership over to GMM: */
+ /* Optimization: RLCMAC-GMMRR-PAGE-IND is 1-to-1 ABI compatible with
+ GMM-GMMRR-PAGE-IND, we just need to adapt the header.
+ See osmo_static_assert(_gmmrr_prim_size) above.
+ */
+ gmm_prim = (struct osmo_gprs_gmm_prim *)rlcmac_prim;
+ gmm_prim->oph.sap = OSMO_GPRS_GMM_SAP_GMMRR;
+ gmm_prim->oph.primitive = OSMO_GPRS_RLCMAC_GMMRR_PAGE;
+ osmo_gprs_gmm_prim_lower_up(gmm_prim);
+ rc = 1; /* Tell RLCMAC that we take ownership of the prim. */
+ break;
+ case OSMO_GPRS_RLCMAC_GMMRR_LLC_TRANSMITTED:
+ /* Forward it to upper layers, pass ownership over to GMM: */
+ /* Optimization: RLCMAC-GMMRR-LLC-TRANSMITTED-IND is 1-to-1 ABI compatible with
+ GMM-GMMRR-LLC-TRANSMITTED-IND, we just need to adapt the header.
+ See osmo_static_assert(_gmmrr_prim_size) above.
+ */
+ gmm_prim = (struct osmo_gprs_gmm_prim *)rlcmac_prim;
+ gmm_prim->oph.sap = OSMO_GPRS_GMM_SAP_GMMRR;
+ gmm_prim->oph.primitive = OSMO_GPRS_GMM_GMMRR_LLC_TRANSMITTED;
+ osmo_gprs_gmm_prim_lower_up(gmm_prim);
+ rc = 1; /* Tell RLCMAC that we take ownership of the prim. */
+ break;
+ default:
+ LOGP(DRLCMAC, LOGL_NOTICE, "%s(): Unexpected Rx RLCMAC GMMRR prim %u\n",
+ __func__, rlcmac_prim->oph.primitive);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int modem_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim);
+ int rc = 0;
+
+ switch (rlcmac_prim->oph.sap) {
+ case OSMO_GPRS_RLCMAC_SAP_GRR:
+ LOGP(DRLCMAC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x ll=[%s]\n",
+ __func__, pdu_name, rlcmac_prim->grr.tlli,
+ osmo_hexdump(rlcmac_prim->grr.ll_pdu, rlcmac_prim->grr.ll_pdu_len));
+ rc = modem_rlcmac_handle_grr(rlcmac_prim);
+ break;
+ case OSMO_GPRS_RLCMAC_SAP_GMMRR:
+ LOGP(DRLCMAC, LOGL_DEBUG, "%s(): Rx %s\n",
+ __func__, pdu_name);
+ rc = modem_rlcmac_handle_gmmrr(rlcmac_prim);
+ break;
+ default:
+ LOGP(DRLCMAC, LOGL_NOTICE, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+ return rc;
+}
+
+static int modem_rlcmac_prim_down_cb(struct osmo_gprs_rlcmac_prim *prim, void *user_data)
+{
+ struct osmo_gprs_rlcmac_l1ctl_prim *lp = &prim->l1ctl;
+ const char *pdu_name = osmo_gprs_rlcmac_prim_name(prim);
+ struct osmocom_ms *ms = user_data;
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_RACH, PRIM_OP_REQUEST):
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_CHAN_ACCESS_REQ, lp);
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_DATA, PRIM_OP_REQUEST):
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_BLOCK_REQ, lp);
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_UL_TBF, PRIM_OP_REQUEST):
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_UL_TBF_CFG_REQ, lp);
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST):
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_DL_TBF_CFG_REQ, lp);
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_ESTABLISH, PRIM_OP_REQUEST):
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_ESTABLISH_REQ, lp);
+ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_RELEASE, PRIM_OP_REQUEST):
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_RELEASE_REQ, lp);
+ default:
+ LOGP(DRLCMAC, LOGL_DEBUG, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+}
+
+static int l1ctl_ul_block_cnf_cb(struct osmocom_ms *ms, struct msgb *msg)
+{
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_BLOCK_CNF, msg);
+}
+
+static int l1ctl_dl_block_ind_cb(struct osmocom_ms *ms, struct msgb *msg)
+{
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_BLOCK_IND, msg);
+}
+
+static int l1ctl_rts_ind_cb(struct osmocom_ms *ms, struct msgb *msg)
+{
+ return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_RTS_IND, msg);
+}
+
+int modem_rlcmac_init(struct osmocom_ms *ms)
+{
+ int rc;
+ rc = osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS);
+ if (rc != 0)
+ return rc;
+
+ osmo_gprs_rlcmac_set_log_cat(OSMO_GPRS_RLCMAC_LOGC_RLCMAC, DRLCMAC);
+ osmo_gprs_rlcmac_set_log_cat(OSMO_GPRS_RLCMAC_LOGC_TBFUL, DRLCMAC);
+ osmo_gprs_rlcmac_set_log_cat(OSMO_GPRS_RLCMAC_LOGC_TBFDL, DRLCMAC);
+
+ osmo_gprs_rlcmac_prim_set_up_cb(modem_rlcmac_prim_up_cb, ms);
+ osmo_gprs_rlcmac_prim_set_down_cb(modem_rlcmac_prim_down_cb, ms);
+
+ ms->l1_entity.l1_gprs_ul_block_cnf = &l1ctl_ul_block_cnf_cb;
+ ms->l1_entity.l1_gprs_dl_block_ind = &l1ctl_dl_block_ind_cb;
+ ms->l1_entity.l1_gprs_rts_ind = &l1ctl_rts_ind_cb;
+
+ return rc;
+}
diff --git a/src/host/layer23/src/modem/sm.c b/src/host/layer23/src/modem/sm.c
new file mode 100644
index 00000000..1977cd6c
--- /dev/null
+++ b/src/host/layer23/src/modem/sm.c
@@ -0,0 +1,277 @@
+/* GPRS SM interfaces as per 3GPP TS 24.008, TS 24.007 */
+/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin@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/lienses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/tun.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+#include <osmocom/gprs/gmm/gmm.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>
+#include <osmocom/gprs/sndcp/sndcp.h>
+#include <osmocom/gprs/sndcp/sndcp_prim.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+
+#include <osmocom/bb/common/settings.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/modem/sm.h>
+
+
+static int modem_sm_handle_pdp_act_cnf(struct osmocom_ms *ms, struct osmo_gprs_sm_prim *sm_prim)
+{
+ const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim);
+ struct osmobb_apn *apn = NULL, *apn_it;
+ struct osmo_netdev *netdev;
+ char buf_addr[INET6_ADDRSTRLEN];
+ char buf_addr2[INET6_ADDRSTRLEN];
+ int rc;
+
+ llist_for_each_entry(apn_it, &ms->gprs.apn_list, list) {
+ if (apn_it->fsm.fi->state == APN_ST_ACTIVATING) {
+ apn = apn_it;
+ break;
+ }
+ }
+
+ if (!apn) {
+ LOGP(DSM, LOGL_ERROR, "Rx %s but have no APN!\n", pdu_name);
+ return -ENOENT;
+ }
+
+ if (!sm_prim->smreg.pdp_act_cnf.accepted) {
+ LOGPAPN(LOGL_ERROR, apn, "Rx %s: Activate PDP failed! cause '%s'\n", pdu_name,
+ get_value_string(gsm48_gsm_cause_names, sm_prim->smreg.pdp_act_cnf.rej.cause));
+ osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_REJ, NULL);
+ /* TODO: maybe retry ? */
+ return 0;
+ }
+
+ ms->subscr.gprs.ptmsi = sm_prim->smreg.pdp_act_cnf.acc.gmm.allocated_ptmsi;
+ ms->gmmlayer.tlli = sm_prim->smreg.pdp_act_cnf.acc.gmm.allocated_tlli;
+
+ apn->pdp.pdp_addr_ietf_type = sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_ietf_type;
+
+ netdev = osmo_tundev_get_netdev(apn->tun);
+ switch (sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_ietf_type) {
+ case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4:
+ LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv4=%s\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr));
+ memcpy(&apn->pdp.pdp_addr_v4, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, sizeof(struct osmo_sockaddr));
+ rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, 30);
+ if (rc < 0) {
+ LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv4=%s\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr));
+ return rc;
+ }
+ break;
+ case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6:
+ LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv6=%s [FIXME: IPv6 not yet supported!]\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr));
+ memcpy(&apn->pdp.pdp_addr_v6, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, sizeof(struct osmo_sockaddr));
+ rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, 64);
+ if (rc < 0) {
+ LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv6=%s\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr));
+ return rc;
+ }
+ break;
+ case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6:
+ LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv4=%s IPv6=%s [FIXME: IPv6 not yet supported!]\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr),
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr2));
+ memcpy(&apn->pdp.pdp_addr_v4, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, sizeof(struct osmo_sockaddr));
+ memcpy(&apn->pdp.pdp_addr_v6, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, sizeof(struct osmo_sockaddr));
+ rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, 30);
+ if (rc < 0) {
+ LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv4=%s\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr));
+ return rc;
+ }
+ rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, 64);
+ if (rc < 0) {
+ LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv6=%s\n", pdu_name,
+ osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr));
+ return rc;
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ /* TODO: Handle PCO */
+ /* TODO: Handle QoS */
+
+ osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_ACC, NULL);
+ return rc;
+}
+
+static int modem_sm_prim_up_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim);
+ struct osmocom_ms *ms = user_data;
+ int rc = 0;
+
+ switch (sm_prim->oph.sap) {
+ case OSMO_GPRS_SM_SAP_SMREG:
+ switch (OSMO_PRIM_HDR(&sm_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_CONFIRM):
+ modem_sm_handle_pdp_act_cnf(ms, sm_prim);
+ break;
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_INDICATION):
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, PRIM_OP_CONFIRM):
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, PRIM_OP_INDICATION):
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_MODIFY, PRIM_OP_CONFIRM):
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_MODIFY, PRIM_OP_INDICATION):
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE_SEC, PRIM_OP_CONFIRM):
+ default:
+ LOGP(DSM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name);
+ break;
+ };
+ break;
+ default:
+ LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ return rc;
+}
+
+int modem_sm_prim_sndcp_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim);
+ int rc;
+
+ switch (sndcp_prim->oph.sap) {
+ case OSMO_GPRS_SNDCP_SAP_SNSM:
+ switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_ACTIVATE, PRIM_OP_INDICATION):
+ LOGP(DSM, LOGL_INFO, "%s(): Rx %s\n", __func__, pdu_name);
+ rc = osmo_gprs_sndcp_prim_dispatch_snsm(sndcp_prim);
+ rc = 1; /* Tell SM that we take ownership of the prim. */
+ break;
+ default:
+ LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+ break;
+ default:
+ LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+ return rc;
+}
+
+static int modem_sm_prim_down_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim);
+ int rc = 0;
+
+ switch (sm_prim->oph.sap) {
+ default:
+ LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ return rc;
+}
+
+static int modem_sm_prim_gmm_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data)
+{
+ int rc;
+
+ rc = osmo_gprs_gmm_prim_upper_down(gmm_prim);
+
+ /* GMM took ownership of the message, tell SM layer to not free it: */
+ rc = 1;
+ return rc;
+}
+
+int modem_sm_init(struct osmocom_ms *ms)
+{
+ int rc;
+ rc = osmo_gprs_sm_init(OSMO_GPRS_SM_LOCATION_MS);
+ if (rc != 0)
+ return rc;
+
+ osmo_gprs_sm_set_log_cat(OSMO_GPRS_SM_LOGC_SM, DSM);
+
+ osmo_gprs_sm_prim_set_up_cb(modem_sm_prim_up_cb, ms);
+ osmo_gprs_sm_prim_set_sndcp_up_cb(modem_sm_prim_sndcp_up_cb, ms);
+ osmo_gprs_sm_prim_set_down_cb(modem_sm_prim_down_cb, ms);
+ osmo_gprs_sm_prim_set_gmm_down_cb(modem_sm_prim_gmm_down_cb, ms);
+
+ return rc;
+}
+
+int modem_sm_smreg_pdp_act_req(const struct osmocom_ms *ms, const struct osmobb_apn *apn)
+{
+ struct osmo_gprs_sm_prim *sm_prim;
+ const struct gsm_subscriber *subscr = &ms->subscr;
+ enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type;
+ struct osmo_sockaddr pdp_addr_any = {0};
+ int rc;
+
+ if (apn->cfg.apn_type_mask & APN_TYPE_IPv4v6) {
+ pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6;
+ } else if (apn->cfg.apn_type_mask & APN_TYPE_IPv4) {
+ pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4;
+ } else if (apn->cfg.apn_type_mask & APN_TYPE_IPv6) {
+ pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6;
+ } else {
+ LOGP(DSM, LOGL_ERROR, "APN '%s' has no PDP address type set\n", apn->cfg.name);
+ return -EINVAL;
+ }
+
+ sm_prim = osmo_gprs_sm_prim_alloc_smreg_pdp_act_req();
+ sm_prim->smreg.pdp_act_req.nsapi = apn->pdp.nsapi;
+ sm_prim->smreg.pdp_act_req.llc_sapi = apn->pdp.llc_sapi;
+ sm_prim->smreg.pdp_act_req.pdp_addr_ietf_type = pdp_addr_ietf_type;
+ sm_prim->smreg.pdp_act_req.pdp_addr_v4 = pdp_addr_any;
+ sm_prim->smreg.pdp_act_req.pdp_addr_v6 = pdp_addr_any;
+ memcpy(sm_prim->smreg.pdp_act_req.qos, apn->pdp.qos, apn->pdp.qos_len);
+ sm_prim->smreg.pdp_act_req.qos_len = apn->pdp.qos_len;
+ memcpy(sm_prim->smreg.pdp_act_req.pco, apn->pdp.pco, apn->pdp.pco_len);
+ sm_prim->smreg.pdp_act_req.pco_len = apn->pdp.pco_len;
+ OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.apn, apn->cfg.name);
+ sm_prim->smreg.pdp_act_req.gmm.ptmsi = subscr->gprs.ptmsi;
+ sm_prim->smreg.pdp_act_req.gmm.ptmsi_sig = subscr->gprs.ptmsi;
+ OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imsi, subscr->imsi);
+ OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imei, ms->settings.imei);
+ OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imeisv, ms->settings.imeisv);
+ memcpy(&sm_prim->smreg.pdp_act_req.gmm.old_rai, &subscr->gprs.rai, sizeof(subscr->gprs.rai));
+
+ rc = osmo_gprs_sm_prim_upper_down(sm_prim);
+ if (rc < 0)
+ LOGP(DSM, LOGL_ERROR, "Failed submitting SMREG-PDP_ACT_REQ.req\n");
+ return rc;
+}
diff --git a/src/host/layer23/src/modem/sndcp.c b/src/host/layer23/src/modem/sndcp.c
new file mode 100644
index 00000000..d5b38213
--- /dev/null
+++ b/src/host/layer23/src/modem/sndcp.c
@@ -0,0 +1,217 @@
+/* GPRS SNDCP User/SN/SNSM interfaces as per 3GPP TS 04.65 */
+/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin@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/lienses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/tun.h>
+
+#include <osmocom/gprs/llc/llc.h>
+#include <osmocom/gprs/llc/llc_prim.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+#include <osmocom/gprs/sm/sm.h>
+#include <osmocom/gprs/sndcp/sndcp_prim.h>
+#include <osmocom/gprs/sndcp/sndcp.h>
+
+#include <osmocom/bb/common/settings.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/modem/sndcp.h>
+
+/* Received SN-XID.cnf from SNDCP layer: */
+static int modem_sndcp_handle_sn_xid_cnf(struct osmobb_apn *apn, struct osmo_gprs_sndcp_prim *sndcp_prim)
+{
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx SN-XID.cnf: TODO IMPLEMENT!\n", __func__);
+ return 0;
+}
+
+/* Received SN-UNITDTA.ind from SNDCP layer: */
+static int modem_sndcp_handle_sn_unitdata_ind(struct osmobb_apn *apn, struct osmo_gprs_sndcp_prim *sndcp_prim)
+{
+ const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim);
+ struct msgb *msg;
+ int rc;
+
+ LOGP(DSNDCP, LOGL_DEBUG, "Rx %s TLLI=0x%08x SAPI=%s NSAPI=%u NPDU=[%s]\n",
+ npdu_name,
+ sndcp_prim->sn.tlli, osmo_gprs_llc_sapi_name(sndcp_prim->sn.sapi),
+ sndcp_prim->sn.data_req.nsapi,
+ osmo_hexdump(sndcp_prim->sn.data_ind.npdu, sndcp_prim->sn.data_ind.npdu_len));
+
+ msg = msgb_alloc(sndcp_prim->sn.data_ind.npdu_len, "tx_tun");
+ memcpy(msgb_put(msg, sndcp_prim->sn.data_ind.npdu_len),
+ sndcp_prim->sn.data_ind.npdu,
+ sndcp_prim->sn.data_ind.npdu_len);
+ rc = osmo_tundev_send(apn->tun, msg);
+ return rc;
+}
+
+static int modem_sndcp_prim_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
+{
+ struct osmocom_ms *ms = user_data;
+ struct osmobb_apn *apn = NULL, *apn_it;
+ const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim);
+ int rc = 0;
+
+ if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SN) {
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, npdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ if (ms->gmmlayer.tlli != sndcp_prim->sn.tlli) {
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx %s: MS has no TLLI=0x%08x\n", __func__, npdu_name, sndcp_prim->sn.tlli);
+ return -ENOENT;
+ }
+
+ llist_for_each_entry(apn_it, &ms->gprs.apn_list, list) {
+ if (apn_it->pdp.nsapi != sndcp_prim->sn.unitdata_ind.nsapi)
+ continue;
+ apn = apn_it;
+ break;
+ }
+ if (!apn) {
+ LOGP(DSNDCP, LOGL_NOTICE, "Unable to find destination APN: Rx %s\n", npdu_name);
+ return -ENODEV;
+ }
+
+ switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_UNITDATA, PRIM_OP_INDICATION):
+ rc = modem_sndcp_handle_sn_unitdata_ind(apn, sndcp_prim);
+ break;
+ case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_XID, PRIM_OP_CONFIRM):
+ rc = modem_sndcp_handle_sn_xid_cnf(apn, sndcp_prim);
+ break;
+ default:
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, npdu_name);
+ break;
+ };
+ return rc;
+}
+
+static int modem_sndcp_prim_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data)
+{
+ const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim);
+ int rc = 0;
+
+ if (llc_prim->oph.sap != OSMO_GPRS_LLC_SAP_LL) {
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ switch (OSMO_PRIM_HDR(&llc_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_REQUEST):
+ case OSMO_PRIM(OSMO_GPRS_LLC_LL_XID, PRIM_OP_REQUEST):
+ LOGP(DSNDCP, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x SAPI=%s L3=[%s]\n",
+ __func__, pdu_name,
+ llc_prim->ll.tlli, osmo_gprs_llc_sapi_name(llc_prim->ll.sapi),
+ osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len));
+ rc = osmo_gprs_llc_prim_upper_down(llc_prim);
+ rc = 1; /* Tell SNDCP layer we took msgb ownsership and transfer it to LLC */
+ break;
+ default:
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name);
+ break;
+ };
+ return rc;
+}
+
+static int modem_sndcp_prim_snsm_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data)
+{
+ const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim);
+ int rc = 0;
+
+ if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SNSM) {
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, npdu_name);
+ OSMO_ASSERT(0);
+ }
+
+ switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) {
+ case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_RESPONSE):
+ LOGP(DSNDCP, LOGL_INFO, "%s(): Rx %s\n", __func__, npdu_name);
+ rc = osmo_gprs_sm_prim_sndcp_upper_down(sndcp_prim);
+ rc = 1; /* Tell SNDCP layer we took msgb ownership and transfer it to SM */
+ break;
+ default:
+ LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, npdu_name);
+ OSMO_ASSERT(0);
+ }
+ return rc;
+}
+
+
+int modem_sndcp_init(struct osmocom_ms *ms)
+{
+ int rc;
+ rc = osmo_gprs_sndcp_init(OSMO_GPRS_SNDCP_LOCATION_MS);
+ if (rc != 0)
+ return rc;
+
+ osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SNDCP, DSNDCP);
+ osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SLHC, DSNDCP);
+
+ osmo_gprs_sndcp_prim_set_up_cb(modem_sndcp_prim_up_cb, ms);
+ osmo_gprs_sndcp_prim_set_down_cb(modem_sndcp_prim_down_cb, ms);
+ osmo_gprs_sndcp_prim_set_snsm_cb(modem_sndcp_prim_snsm_cb, ms);
+ return rc;
+}
+
+int modem_sndcp_sn_xid_req(struct osmobb_apn *apn)
+{
+ struct osmo_gprs_sndcp_prim *sndcp_prim;
+ int rc;
+ struct osmocom_ms *ms = apn->ms;
+ struct gprs_settings *set = &ms->gprs;
+
+ sndcp_prim = osmo_gprs_sndcp_prim_alloc_sn_xid_req(ms->gmmlayer.tlli,
+ apn->pdp.llc_sapi,
+ apn->pdp.nsapi);
+ OSMO_ASSERT(sndcp_prim);
+ sndcp_prim->sn.xid_req.pcomp_rfc1144.active = set->pcomp_rfc1144.active;
+ sndcp_prim->sn.xid_req.pcomp_rfc1144.s01 = set->pcomp_rfc1144.s01;
+ sndcp_prim->sn.xid_req.dcomp_v42bis.active = set->dcomp_v42bis.active;
+ sndcp_prim->sn.xid_req.dcomp_v42bis.p0 = set->dcomp_v42bis.p0;
+ sndcp_prim->sn.xid_req.dcomp_v42bis.p1 = set->dcomp_v42bis.p1;
+ sndcp_prim->sn.xid_req.dcomp_v42bis.p2 = set->dcomp_v42bis.p2;
+ rc = osmo_gprs_sndcp_prim_upper_down(sndcp_prim);
+ return rc;
+}
+
+int modem_sndcp_sn_unitdata_req(struct osmobb_apn *apn, uint8_t *npdu, size_t npdu_len)
+{
+ struct osmo_gprs_sndcp_prim *sndcp_prim;
+ int rc;
+
+ sndcp_prim = osmo_gprs_sndcp_prim_alloc_sn_unitdata_req(apn->ms->gmmlayer.tlli,
+ apn->pdp.llc_sapi,
+ apn->pdp.nsapi,
+ npdu, npdu_len);
+ OSMO_ASSERT(sndcp_prim);
+ rc = osmo_gprs_sndcp_prim_upper_down(sndcp_prim);
+ return rc;
+}
diff --git a/src/host/layer23/src/modem/vty.c b/src/host/layer23/src/modem/vty.c
new file mode 100644
index 00000000..f42aa669
--- /dev/null
+++ b/src/host/layer23/src/modem/vty.c
@@ -0,0 +1,450 @@
+/*
+ * (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
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/gprs/llc/llc.h>
+#include <osmocom/gprs/llc/llc_prim.h>
+#include <osmocom/gprs/gmm/gmm_prim.h>
+#include <osmocom/gprs/sm/sm_prim.h>
+#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
+
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+
+#include <osmocom/bb/common/settings.h>
+#include <osmocom/bb/common/vty.h>
+#include <osmocom/bb/common/apn.h>
+#include <osmocom/bb/common/ms.h>
+#include <osmocom/bb/modem/gmm.h>
+#include <osmocom/bb/modem/grr.h>
+#include <osmocom/bb/modem/sm.h>
+#include <osmocom/bb/modem/vty.h>
+
+static struct cmd_node apn_node = {
+ APN_NODE,
+ "%s(apn)# ",
+ 1
+};
+
+int modem_vty_go_parent(struct vty *vty)
+{
+ struct osmobb_apn *apn;
+
+ switch (vty->node) {
+ case APN_NODE:
+ apn = vty->index;
+ vty->index = apn->ms;
+ vty->node = MS_NODE;
+ break;
+ }
+ return vty->node;
+}
+
+#define MS_NAME_DESC "Name of MS (see \"show ms\")\n"
+#define TEST_CMD_DESC "Testing commands for developers\n"
+#define GRR_CMDG_DESC "GPRS RR specific commands\n"
+#define LLC_CMDG_DESC "GPRS LLC specific commands\n"
+#define GMM_CMDG_DESC "GPRS GMM specific commands\n"
+#define SM_CMDG_DESC "GPRS SM specific commands\n"
+
+/* testing commands */
+DEFUN_HIDDEN(test_grr_tx_chan_req,
+ test_grr_tx_chan_req_cmd,
+ "test MS_NAME grr start-chan-access (1phase|2phase)",
+ TEST_CMD_DESC MS_NAME_DESC GRR_CMDG_DESC
+ "Send a CHANNEL REQUEST (RACH) to the network\n"
+ "One-phase packet access (011110xx or 01111x0x or 01111xx0)\n"
+ "Two-phase (single block) packet access (01110xxx)\n")
+{
+ struct osmocom_ms *ms;
+
+ if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL)
+ return CMD_WARNING;
+
+ const struct osmo_gprs_rlcmac_l1ctl_prim lp = {
+ .rach_req = {
+ .is_11bit = false,
+ /* the 3 LSBs are randomized during (re)transmission */
+ .ra = argv[1][0] == '1' ? 0x78 : 0x70,
+ }
+ };
+
+ if (osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_CHAN_ACCESS_REQ, (void *)&lp)) {
+ vty_out(vty, "Failed to send a CHANNEL REQUEST%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(test_llc_unitdata_req_hexpdu,
+ test_llc_unitdata_req_hexpdu_cmd,
+ "test MS_NAME llc unitdata-req <0x00-0xffffffff> SAPI HEXSTRING",
+ TEST_CMD_DESC MS_NAME_DESC LLC_CMDG_DESC
+ "Enqueue an LLC UNITDATA.req for transmission\n"
+ "TLLI (Temporary Logical Link Identifier) value to be used\n"
+ "SAPI value to be used (for example, GMM, SMS, SNDCP3)\n"
+ "LLC PDU as a hexstring (up to 512 octets)\n")
+{
+ struct osmo_gprs_llc_prim *llc_prim;
+ struct osmocom_ms *ms;
+ uint8_t buf[512];
+ int tlli, sapi, pdu_len;
+
+ if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL)
+ return CMD_WARNING;
+
+ if (osmo_str_to_int(&tlli, argv[1], 0, 0, 0xffffff) < 0)
+ return CMD_WARNING;
+ sapi = get_string_value(osmo_gprs_llc_sapi_names, argv[2]);
+ if (sapi < 0)
+ return CMD_WARNING;
+ pdu_len = osmo_hexparse(argv[3], &buf[0], sizeof(buf));
+ if (pdu_len < 0)
+ return CMD_WARNING;
+
+ llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(tlli, sapi, &buf[0], pdu_len);
+ if (osmo_gprs_llc_prim_upper_down(llc_prim) != 0) {
+ vty_out(vty, "Failed to enqueue an LLC PDU%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static uint8_t pdu_gmmm_attach_req[] = {
+ 0x08, 0x01, 0x02, 0xe5, 0xe0, 0x01, 0x0a, 0x00, 0x05, 0xf4, 0xf4, 0x3c, 0xec, 0x71, 0x32, 0xf4,
+ 0x07, 0x00, 0x05, 0x00, 0x17, 0x19, 0x33, 0x43, 0x2b, 0x37, 0x15, 0x9e, 0xf9, 0x88, 0x79, 0xcb,
+ 0xa2, 0x8c, 0x66, 0x21, 0xe7, 0x26, 0x88, 0xb1, 0x98, 0x87, 0x9c, 0x00, 0x17, 0x05,
+};
+
+/* TODO: remove this command once we have the GMM layer implemented */
+DEFUN_HIDDEN(test_llc_unitdata_req_gmm_attch,
+ test_llc_unitdata_req_gmm_attch_cmd,
+ "test MS_NAME llc unitdata-req gmm-attach-req",
+ TEST_CMD_DESC MS_NAME_DESC LLC_CMDG_DESC
+ "Enqueue an LLC UNITDATA.req for transmission\n"
+ "Hard-coded GMM Attach Request (SAPI=GMM, TLLI=0xe1c5d364)\n")
+{
+ struct osmo_gprs_llc_prim *llc_prim;
+ const uint32_t tlli = 0xe1c5d364;
+ struct osmocom_ms *ms;
+
+ if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL)
+ return CMD_WARNING;
+
+ llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(tlli, OSMO_GPRS_LLC_SAPI_GMM,
+ &pdu_gmmm_attach_req[0],
+ sizeof(pdu_gmmm_attach_req));
+ if (osmo_gprs_llc_prim_upper_down(llc_prim) != 0) {
+ vty_out(vty, "Failed to enqueue an LLC PDU%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(test_gmm_reg_attach,
+ test_gmm_reg_attach_cmd,
+ "test MS_NAME gmm attach",
+ TEST_CMD_DESC MS_NAME_DESC GMM_CMDG_DESC
+ "Enqueue a GMM GMMREG-ATTACH.req for transmission\n")
+{
+ struct osmocom_ms *ms;
+
+ if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) {
+ vty_out(vty, "Failed to find ms '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (modem_gmm_gmmreg_attach_req(ms) < 0) {
+ vty_out(vty, "Failed to enqueue a GMM PDU%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(test_gmm_reg_detach,
+ test_gmm_reg_detach_cmd,
+ "test MS_NAME gmm detach",
+ TEST_CMD_DESC MS_NAME_DESC GMM_CMDG_DESC
+ "Enqueue a GMM GMMREG-DETACH.req for transmission\n")
+{
+ struct osmocom_ms *ms;
+
+ if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) {
+ vty_out(vty, "Failed to find ms '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (modem_gmm_gmmreg_detach_req(ms) < 0) {
+ vty_out(vty, "Failed to enqueue a GMM PDU%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(test_sm_act_pdp_ctx,
+ test_sm_act_pdp_ctx_cmd,
+ "test MS_NAME sm act-pdp-ctx APN",
+ TEST_CMD_DESC MS_NAME_DESC SM_CMDG_DESC
+ "Enqueue a SM SMREG-ACTIVATE.req for transmission\n"
+ "APN to activate\n")
+{
+ struct osmocom_ms *ms;
+ struct osmobb_apn *apn;
+
+ if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) {
+ vty_out(vty, "Unable to find MS '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ apn = ms_find_apn_by_name(ms, argv[1]);
+ if (!apn) {
+ vty_out(vty, "Unable to find APN '%s'%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (modem_sm_smreg_pdp_act_req(ms, apn) < 0) {
+ vty_out(vty, "Failed submitting SM PDP Act Req%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* per APN config */
+DEFUN(cfg_ms_apn, cfg_ms_apn_cmd, "apn APN_NAME",
+ "Configure an APN\n"
+ "Name of APN\n")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct osmobb_apn *apn;
+
+ apn = ms_find_apn_by_name(ms, argv[0]);
+ if (!apn)
+ apn = apn_alloc(ms, argv[0]);
+ if (!apn) {
+ vty_out(vty, "Unable to create APN '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty->index = apn;
+ vty->node = APN_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_no_apn, cfg_ms_no_apn_cmd, "no apn APN_NAME",
+ NO_STR "Configure an APN\n"
+ "Name of APN\n")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct osmobb_apn *apn;
+ bool on = false;
+
+ apn = ms_find_apn_by_name(ms, argv[0]);
+ if (!apn) {
+ vty_out(vty, "Unable to find APN '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Disable APN before getting rid of it. */
+ osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on);
+
+ apn_free(apn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
+ "tun-device NAME",
+ "Configure tun device name\n"
+ "TUN device name")
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->cfg.dev_name, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_tun_netns_name, cfg_apn_tun_netns_name_cmd,
+ "tun-netns NAME",
+ "Configure tun device network namespace name\n"
+ "TUN device network namespace name")
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->cfg.dev_netns_name, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_tun_netns_name, cfg_apn_no_tun_netns_name_cmd,
+ "no tun-netns",
+ "Configure tun device to use default network namespace name\n")
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+ TALLOC_FREE(apn->cfg.dev_netns_name);
+ return CMD_SUCCESS;
+}
+
+static const struct value_string pdp_type_names[] = {
+ { APN_TYPE_IPv4, "v4" },
+ { APN_TYPE_IPv6, "v6" },
+ { APN_TYPE_IPv4v6, "v4v6" },
+ { 0, NULL }
+};
+
+#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
+ "IPv6(-only) PDP Type\n" \
+ "IPv4v6 (dual-stack) PDP Type\n"
+
+DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
+ "type-support (v4|v6|v4v6)",
+ "Enable support for PDP Type\n"
+ V4V6V46_STRING)
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+ uint32_t type = get_string_value(pdp_type_names, argv[0]);
+
+ apn->cfg.apn_type_mask |= type;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
+ "shutdown",
+ "Put the APN in administrative shut-down\n")
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+ bool on = false;
+ int rc;
+
+ if (!apn->cfg.shutdown) {
+ rc = osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on);
+ if (rc < 0) {
+ vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ apn->cfg.shutdown = true;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
+ "no shutdown",
+ NO_STR "Remove the APN from administrative shut-down\n")
+{
+ struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
+ bool on = true;
+ int rc;
+
+ if (apn->cfg.shutdown) {
+ if (!apn->cfg.dev_name) {
+ vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ rc = osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on);
+ if (rc < 0) {
+ vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ apn->cfg.shutdown = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void config_write_apn(struct vty *vty, const struct osmobb_apn *apn)
+{
+ unsigned int i;
+
+ vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
+
+ if (apn->cfg.dev_name)
+ vty_out(vty, " tun-device %s%s", apn->cfg.dev_name, VTY_NEWLINE);
+ if (apn->cfg.dev_netns_name)
+ vty_out(vty, " tun-netns %s%s", apn->cfg.dev_netns_name, VTY_NEWLINE);
+
+ for (i = 0; i < 32; i++) {
+ if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
+ continue;
+ vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
+ VTY_NEWLINE);
+ }
+
+ /* must be last */
+ vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
+}
+
+static void config_write_ms(struct vty *vty, const struct osmocom_ms *ms)
+{
+ struct osmobb_apn *apn;
+
+ vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE);
+
+ l23_vty_config_write_ms_node_contents(vty, ms, " ");
+
+ llist_for_each_entry(apn, &ms->gprs.apn_list, list)
+ config_write_apn(vty, apn);
+
+ l23_vty_config_write_ms_node_contents_final(vty, ms, " ");
+}
+
+static int config_write(struct vty *vty)
+{
+ struct osmocom_ms *ms;
+ llist_for_each_entry(ms, &ms_list, entity)
+ config_write_ms(vty, ms);
+ return CMD_SUCCESS;
+}
+
+int modem_vty_init(void)
+{
+ int rc;
+
+ if ((rc = l23_vty_init(config_write, NULL)) < 0)
+ return rc;
+ install_element_ve(&l23_show_ms_cmd);
+ install_element_ve(&test_grr_tx_chan_req_cmd);
+ install_element_ve(&test_llc_unitdata_req_hexpdu_cmd);
+ install_element_ve(&test_llc_unitdata_req_gmm_attch_cmd);
+ install_element_ve(&test_gmm_reg_attach_cmd);
+ install_element_ve(&test_gmm_reg_detach_cmd);
+ install_element_ve(&test_sm_act_pdp_ctx_cmd);
+ install_element(CONFIG_NODE, &l23_cfg_ms_cmd);
+
+ install_element(MS_NODE, &cfg_ms_apn_cmd);
+ install_element(MS_NODE, &cfg_ms_no_apn_cmd);
+ install_node(&apn_node, NULL);
+ install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
+ install_element(APN_NODE, &cfg_apn_tun_netns_name_cmd);
+ install_element(APN_NODE, &cfg_apn_no_tun_netns_name_cmd);
+ install_element(APN_NODE, &cfg_apn_type_support_cmd);
+ install_element(APN_NODE, &cfg_apn_shutdown_cmd);
+ install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
+
+ return 0;
+}
diff --git a/src/host/osmocon/configure.ac b/src/host/osmocon/configure.ac
index a42f4874..4195ecd7 100644
--- a/src/host/osmocon/configure.ac
+++ b/src/host/osmocon/configure.ac
@@ -5,6 +5,8 @@ AC_INIT([osmocon],
AM_INIT_AUTOMAKE([dist-bzip2])
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -14,7 +16,7 @@ AC_PROG_CC
AC_PROG_INSTALL
dnl checks for libraries
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
dnl checks for header files
AC_HEADER_STDC
@@ -42,6 +44,7 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c
index d49c3fdf..547f49fe 100644
--- a/src/host/osmocon/osmocon.c
+++ b/src/host/osmocon/osmocon.c
@@ -16,10 +16,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 <ctype.h>
diff --git a/src/host/osmocon/osmoload.c b/src/host/osmocon/osmoload.c
index d320b298..b2f14bbd 100644
--- a/src/host/osmocon/osmoload.c
+++ b/src/host/osmocon/osmoload.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
diff --git a/src/host/osmocon/tpu_debug.c b/src/host/osmocon/tpu_debug.c
index 9160b014..82c8d30f 100644
--- a/src/host/osmocon/tpu_debug.c
+++ b/src/host/osmocon/tpu_debug.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore
index fe90e43c..b5c3a99d 100644
--- a/src/host/trxcon/.gitignore
+++ b/src/host/trxcon/.gitignore
@@ -9,6 +9,11 @@ install-sh
missing
compile
+# libtool by-products
+ltmain.sh
+libtool
+m4/*.m4
+
# configure by-products
.deps/
Makefile
@@ -18,9 +23,11 @@ version.h
# build by-products
*.o
+*.lo
*.a
+*.la
-trxcon
+/src/trxcon
# various
.version
diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am
index b51db02f..5b1002c8 100644
--- a/src/host/trxcon/Makefile.am
+++ b/src/host/trxcon/Makefile.am
@@ -1,52 +1,21 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
-# versioning magic
-BUILT_SOURCES = $(top_srcdir)/.version
-$(top_srcdir)/.version:
- echo $(VERSION) > $@-t && mv $@-t $@
-dist-hook:
- echo $(VERSION) > $(distdir)/.tarball-version
-
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOCODING_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
+SUBDIRS = \
+ include \
+ src \
$(NULL)
-bin_PROGRAMS = trxcon
+ACLOCAL_AMFLAGS = -I m4
-trxcon_SOURCES = \
- l1ctl_link.c \
- l1ctl.c \
- trx_if.c \
- logging.c \
- trxcon.c \
+BUILT_SOURCES = \
+ $(top_srcdir)/.version \
$(NULL)
-
-# Scheduler
-trxcon_SOURCES += \
- sched_lchan_common.c \
- sched_lchan_pdtch.c \
- sched_lchan_desc.c \
- sched_lchan_xcch.c \
- sched_lchan_tchf.c \
- sched_lchan_tchh.c \
- sched_lchan_rach.c \
- sched_lchan_sch.c \
- sched_mframe.c \
- sched_clck.c \
- sched_prim.c \
- sched_trx.c \
+EXTRA_DIST = \
+ .version \
$(NULL)
-trxcon_LDADD = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOCODING_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(NULL)
+# versioning magic
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac
index 1f24260d..6508689b 100644
--- a/src/host/trxcon/configure.ac
+++ b/src/host/trxcon/configure.ac
@@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script
AC_INIT([trxcon], [0.0.0])
AM_INIT_AUTOMAKE
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -18,6 +20,9 @@ PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm)
dnl checks for header files
AC_HEADER_STDC
+dnl init libtool
+LT_INIT
+
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
[--enable-sanitize],
@@ -29,7 +34,36 @@ then
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
+AC_ARG_ENABLE(werror,
+ [AS_HELP_STRING(
+ [--enable-werror],
+ [Turn all compiler warnings into errors, with exceptions:
+ a) deprecation (allow upstream to mark deprecation without breaking builds);
+ b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
+ ]
+ )],
+ [werror=$enableval], [werror="no"])
+if test x"$werror" = x"yes"
+then
+ WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
+ WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
+ WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
+ CFLAGS="$CFLAGS $WERROR_FLAGS"
+ CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
+fi
+
+AC_MSG_RESULT([CFLAGS="$CFLAGS"])
+AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
+
dnl Checks for typedefs, structures and compiler characteristics
-AC_OUTPUT(
- Makefile)
+AC_CONFIG_MACRO_DIRS([m4])
+AC_CONFIG_FILES([include/Makefile
+ include/osmocom/Makefile
+ include/osmocom/bb/Makefile
+ include/osmocom/bb/l1sched/Makefile
+ include/osmocom/bb/trxcon/Makefile
+ src/Makefile
+ Makefile])
+AC_OUTPUT
diff --git a/src/host/trxcon/include/Makefile.am b/src/host/trxcon/include/Makefile.am
new file mode 100644
index 00000000..9d963a02
--- /dev/null
+++ b/src/host/trxcon/include/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ osmocom \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/Makefile.am b/src/host/trxcon/include/osmocom/Makefile.am
new file mode 100644
index 00000000..83c6385c
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ bb \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/Makefile.am b/src/host/trxcon/include/osmocom/bb/Makefile.am
new file mode 100644
index 00000000..4a575ffb
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = \
+ l1sched \
+ trxcon \
+ $(NULL)
+
+noinst_HEADERS = \
+ l1ctl_proto.h \
+ l1gprs.h \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h b/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h
new file mode 120000
index 00000000..ee19b80e
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h
@@ -0,0 +1 @@
+../../../../../../include/l1ctl_proto.h \ No newline at end of file
diff --git a/src/host/trxcon/include/osmocom/bb/l1gprs.h b/src/host/trxcon/include/osmocom/bb/l1gprs.h
new file mode 120000
index 00000000..3bf85176
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1gprs.h
@@ -0,0 +1 @@
+../../../../../../include/l1gprs.h \ No newline at end of file
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
new file mode 100644
index 00000000..39c32ba0
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
@@ -0,0 +1,5 @@
+noinst_HEADERS = \
+ l1sched.h \
+ logging.h \
+ prim.h \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
new file mode 100644
index 00000000..6c5a31e8
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
@@ -0,0 +1,417 @@
+#pragma once
+
+#include <time.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+#include <osmocom/bb/l1sched/prim.h>
+
+#define GPRS_L2_MAX_LEN 54
+#define EDGE_L2_MAX_LEN 155
+
+#define L1SCHED_CH_LID_DEDIC 0x00
+#define L1SCHED_CH_LID_SACCH 0x40
+
+/* Osmocom-specific extension for PTCCH (see 3GPP TS 45.002, section 3.3.4.2).
+ * Shall be used to distinguish PTCCH and PDTCH channels on a PDCH time-slot. */
+#define L1SCHED_CH_LID_PTCCH 0x80
+
+/* Is a channel related to PDCH (GPRS) */
+#define L1SCHED_CH_FLAG_PDCH (1 << 0)
+/* Should a channel be activated automatically */
+#define L1SCHED_CH_FLAG_AUTO (1 << 1)
+
+#define MAX_A5_KEY_LEN (128 / 8)
+#define TRX_TS_COUNT 8
+
+struct l1sched_lchan_state;
+struct l1sched_meas_set;
+struct l1sched_state;
+struct l1sched_ts;
+
+enum l1sched_burst_type {
+ L1SCHED_BURST_GMSK,
+ L1SCHED_BURST_8PSK,
+};
+
+/**
+ * These types define the different channels on a multiframe.
+ * Each channel has queues and can be activated individually.
+ */
+enum l1sched_lchan_type {
+ L1SCHED_IDLE = 0,
+ L1SCHED_FCCH,
+ L1SCHED_SCH,
+ L1SCHED_BCCH,
+ L1SCHED_RACH,
+ L1SCHED_CCCH,
+ L1SCHED_TCHF,
+ L1SCHED_TCHH_0,
+ L1SCHED_TCHH_1,
+ L1SCHED_SDCCH4_0,
+ L1SCHED_SDCCH4_1,
+ L1SCHED_SDCCH4_2,
+ L1SCHED_SDCCH4_3,
+ L1SCHED_SDCCH8_0,
+ L1SCHED_SDCCH8_1,
+ L1SCHED_SDCCH8_2,
+ L1SCHED_SDCCH8_3,
+ L1SCHED_SDCCH8_4,
+ L1SCHED_SDCCH8_5,
+ L1SCHED_SDCCH8_6,
+ L1SCHED_SDCCH8_7,
+ L1SCHED_SACCHTF,
+ L1SCHED_SACCHTH_0,
+ L1SCHED_SACCHTH_1,
+ L1SCHED_SACCH4_0,
+ L1SCHED_SACCH4_1,
+ L1SCHED_SACCH4_2,
+ L1SCHED_SACCH4_3,
+ L1SCHED_SACCH8_0,
+ L1SCHED_SACCH8_1,
+ L1SCHED_SACCH8_2,
+ L1SCHED_SACCH8_3,
+ L1SCHED_SACCH8_4,
+ L1SCHED_SACCH8_5,
+ L1SCHED_SACCH8_6,
+ L1SCHED_SACCH8_7,
+ L1SCHED_PDTCH,
+ L1SCHED_PTCCH,
+ L1SCHED_SDCCH4_CBCH,
+ L1SCHED_SDCCH8_CBCH,
+ _L1SCHED_CHAN_MAX
+};
+
+/* Represents a burst to be transmitted */
+struct l1sched_burst_req {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t pwr;
+
+ /* Internally used by the scheduler */
+ uint8_t bid;
+
+ ubit_t burst[GSM_NBITS_NB_8PSK_BURST];
+ size_t burst_len;
+};
+
+/* Represents a received burst */
+struct l1sched_burst_ind {
+ uint32_t fn;
+ uint8_t tn;
+
+ /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */
+ int16_t toa256;
+ /*! RSSI (Received Signal Strength Indication) */
+ int8_t rssi;
+
+ /* Internally used by the scheduler */
+ uint8_t bid;
+
+ sbit_t burst[GSM_NBITS_NB_8PSK_BURST];
+ size_t burst_len;
+};
+
+/* Probed lchan is active */
+#define L1SCHED_PROBE_F_ACTIVE (1 << 0)
+
+/* RTR (Ready-to-Receive) probe */
+struct l1sched_probe {
+ uint32_t flags; /* see L1SCHED_PROBE_F_* above */
+ uint32_t fn;
+ uint8_t tn;
+};
+
+typedef int l1sched_lchan_rx_func(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
+
+typedef int l1sched_lchan_tx_func(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
+
+struct l1sched_lchan_desc {
+ /*! Human-readable name */
+ const char *name;
+ /*! Human-readable description */
+ const char *desc;
+
+ /*! Channel Number (like in RSL) */
+ uint8_t chan_nr;
+ /*! Link ID (like in RSL) */
+ uint8_t link_id;
+
+ /*! How much memory do we need to store bursts */
+ size_t burst_buf_size;
+ /*! Channel specific flags */
+ uint8_t flags;
+
+ /*! Function to call when burst received from PHY */
+ l1sched_lchan_rx_func *rx_fn;
+ /*! Function to call when data received from L2 */
+ l1sched_lchan_tx_func *tx_fn;
+};
+
+struct l1sched_tdma_frame {
+ /*! Downlink channel (slot) type */
+ enum l1sched_lchan_type dl_chan;
+ /*! Downlink block ID */
+ uint8_t dl_bid;
+ /*! Uplink channel (slot) type */
+ enum l1sched_lchan_type ul_chan;
+ /*! Uplink block ID */
+ uint8_t ul_bid;
+};
+
+struct l1sched_tdma_multiframe {
+ /*! Channel combination */
+ enum gsm_phys_chan_config chan_config;
+ /*! Human-readable name */
+ const char *name;
+ /*! Repeats how many frames */
+ uint8_t period;
+ /*! Applies to which timeslots */
+ uint8_t slotmask;
+ /*! Contains which lchans */
+ uint64_t lchan_mask;
+ /*! Pointer to scheduling structure */
+ const struct l1sched_tdma_frame *frames;
+};
+
+struct l1sched_meas_set {
+ /*! TDMA frame number of the first burst this set belongs to */
+ uint32_t fn;
+ /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */
+ int16_t toa256;
+ /*! RSSI (Received Signal Strength Indication) */
+ int8_t rssi;
+};
+
+/* Simple ring buffer (up to 24 unique measurements) */
+struct l1sched_lchan_meas_hist {
+ struct l1sched_meas_set buf[24];
+ struct l1sched_meas_set *head;
+};
+
+/* States each channel on a multiframe */
+struct l1sched_lchan_state {
+ /*! Channel type */
+ enum l1sched_lchan_type type;
+ /*! Channel status */
+ uint8_t active;
+ /*! Link to a list of channels */
+ struct llist_head list;
+
+ /*! Burst type: GMSK or 8PSK */
+ enum l1sched_burst_type burst_type;
+ /*! Mask of received bursts */
+ uint32_t rx_burst_mask;
+ /*! Mask of transmitted bursts */
+ uint32_t tx_burst_mask;
+ /*! Burst buffer for RX */
+ sbit_t *rx_bursts;
+ /*! Burst buffer for TX */
+ ubit_t *tx_bursts;
+
+ /*! Queue of Tx primitives */
+ struct llist_head tx_prims;
+ /*! Tx primitive being sent */
+ struct msgb *prim;
+
+ /*! Mode for TCH channels (see GSM48_CMODE_*) */
+ uint8_t tch_mode;
+ /*! Training Sequence Code */
+ uint8_t tsc;
+
+ /*! FACCH/H on downlink */
+ bool dl_ongoing_facch;
+ /*! pending FACCH/H blocks on Uplink */
+ uint8_t ul_facch_blocks;
+
+ /*! Downlink measurements history */
+ struct l1sched_lchan_meas_hist meas_hist;
+ /*! AVG measurements of the last received block */
+ struct l1sched_meas_set meas_avg;
+
+ /*! TDMA loss detection state */
+ struct {
+ /*! Last processed TDMA frame number */
+ uint32_t last_proc;
+ /*! Number of processed TDMA frames */
+ unsigned long num_proc;
+ /*! Number of lost TDMA frames */
+ unsigned long num_lost;
+ } tdma;
+
+ /*! SACCH state */
+ struct {
+ /*! Cached measurement report (last received) */
+ uint8_t mr_cache[GSM_MACBLOCK_LEN];
+ /*! Cache usage counter */
+ uint8_t mr_cache_usage;
+ /*! Was a MR transmitted last time? */
+ bool mr_tx_last;
+ } sacch;
+
+ /* AMR specific */
+ struct {
+ /*! 4 possible codecs for AMR */
+ uint8_t codec[4];
+ /*! Number of possible codecs */
+ uint8_t codecs;
+ /*! Current uplink FT index */
+ uint8_t ul_ft;
+ /*! Current downlink FT index */
+ uint8_t dl_ft;
+ /*! Current uplink CMR index */
+ uint8_t ul_cmr;
+ /*! Current downlink CMR index */
+ uint8_t dl_cmr;
+ /*! If AMR loop is enabled */
+ uint8_t amr_loop;
+ /*! Number of bit error rates */
+ uint8_t ber_num;
+ /*! Sum of bit error rates */
+ float ber_sum;
+ /* last received dtx frame type */
+ uint8_t last_dtx;
+ } amr;
+
+ /*! A5/X encryption state */
+ struct {
+ uint8_t key[MAX_A5_KEY_LEN];
+ uint8_t key_len;
+ uint8_t algo;
+ } a5;
+
+ /* TS that this lchan belongs to */
+ struct l1sched_ts *ts;
+};
+
+struct l1sched_ts {
+ /*! Timeslot index within a frame (0..7) */
+ uint8_t index;
+
+ /*! Pointer to multiframe layout */
+ const struct l1sched_tdma_multiframe *mf_layout;
+ /*! Channel states for logical channels */
+ struct llist_head lchans;
+ /*! Backpointer to the scheduler */
+ struct l1sched_state *sched;
+};
+
+/*! Scheduler configuration */
+struct l1sched_cfg {
+ /*! Logging context (used as prefix for messages) */
+ const char *log_prefix;
+};
+
+/*! One scheduler instance */
+struct l1sched_state {
+ /*! List of timeslots maintained by this scheduler */
+ struct l1sched_ts *ts[TRX_TS_COUNT];
+ /*! SACCH cache (common for all lchans) */
+ uint8_t sacch_cache[GSM_MACBLOCK_LEN];
+ /*! BSIC value learned from SCH bursts */
+ uint8_t bsic;
+ /*! Logging context (used as prefix for messages) */
+ const char *log_prefix;
+ /*! Some private data */
+ void *priv;
+};
+
+extern const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX];
+const struct l1sched_tdma_multiframe *
+l1sched_mframe_layout(enum gsm_phys_chan_config config, uint8_t tn);
+
+/* Scheduler management functions */
+struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv);
+void l1sched_reset(struct l1sched_state *sched, bool reset_clock);
+void l1sched_free(struct l1sched_state *sched);
+
+/* Timeslot management functions */
+struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn);
+void l1sched_del_ts(struct l1sched_state *sched, int tn);
+int l1sched_reset_ts(struct l1sched_state *sched, int tn);
+int l1sched_configure_ts(struct l1sched_state *sched, int tn,
+ enum gsm_phys_chan_config config);
+int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo,
+ const uint8_t *key, uint8_t key_len);
+
+/* Logical channel management functions */
+enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr);
+
+void l1sched_deactivate_all_lchans(struct l1sched_ts *ts);
+int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr,
+ int active, uint8_t tch_mode, uint8_t tsc);
+int l1sched_lchan_set_amr_cfg(struct l1sched_lchan_state *lchan,
+ uint8_t codecs_bitmask, uint8_t start_codec);
+int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan);
+int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan);
+struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts,
+ enum l1sched_lchan_type type);
+struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched,
+ uint8_t chan_nr, uint8_t link_id);
+
+#define L1SCHED_TCH_MODE_IS_SPEECH(mode) \
+ (mode == GSM48_CMODE_SPEECH_V1 \
+ || mode == GSM48_CMODE_SPEECH_EFR \
+ || mode == GSM48_CMODE_SPEECH_AMR)
+
+#define L1SCHED_TCH_MODE_IS_DATA(mode) \
+ (mode == GSM48_CMODE_DATA_14k5 \
+ || mode == GSM48_CMODE_DATA_12k0 \
+ || mode == GSM48_CMODE_DATA_6k0 \
+ || mode == GSM48_CMODE_DATA_3k6)
+
+#define L1SCHED_CHAN_IS_TCH(chan) \
+ (chan == L1SCHED_TCHF || chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1)
+
+#define L1SCHED_CHAN_IS_SACCH(chan) \
+ (l1sched_lchan_desc[chan].link_id & L1SCHED_CH_LID_SACCH)
+
+int l1sched_handle_rx_burst(struct l1sched_state *sched,
+ struct l1sched_burst_ind *bi);
+int l1sched_handle_rx_probe(struct l1sched_state *sched,
+ struct l1sched_probe *probe);
+int l1sched_handle_burst_req(struct l1sched_state *sched,
+ const struct l1sched_burst_req *br);
+
+/* Shared declarations for lchan handlers */
+extern const uint8_t l1sched_nb_training_bits[8][26];
+
+const char *l1sched_burst_mask2str(const uint32_t *mask, int bits);
+
+/* Interleaved TCH/H block TDMA frame mapping */
+bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan,
+ uint32_t fn, bool ul, bool facch, bool start);
+
+#define l1sched_tchh_traffic_start(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 0, 1)
+#define l1sched_tchh_traffic_end(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 0, 0)
+
+#define l1sched_tchh_facch_start(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
+#define l1sched_tchh_facch_end(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
+
+/* Measurement history */
+void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
+void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n);
+
+/* Clock and Downlink scheduling trigger */
+int l1sched_clck_handle(struct l1sched_state *sched, uint32_t fn);
+void l1sched_clck_reset(struct l1sched_state *sched);
+
+void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br);
+void l1sched_pull_send_frame(struct l1sched_state *sched);
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/logging.h b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
new file mode 100644
index 00000000..21be9955
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
@@ -0,0 +1,37 @@
+#pragma once
+
+extern int l1sched_log_cat_common;
+extern int l1sched_log_cat_data;
+
+void l1sched_logging_init(int log_cat_common, int log_cat_data);
+
+/* Messages using l1sched_state as the context */
+#define LOGP_SCHED_CAT(sched, cat, level, fmt, args...) \
+ LOGP(l1sched_log_cat_##cat, level, "%s" fmt, \
+ (sched)->log_prefix, ## args)
+
+/* Common messages using l1sched_state as the context */
+#define LOGP_SCHEDC(sched, level, fmt, args...) \
+ LOGP_SCHED_CAT(sched, common, level, fmt, ## args)
+
+/* Data messages using l1sched_state as the context */
+#define LOGP_SCHEDD(sched, level, fmt, args...) \
+ LOGP_SCHED_CAT(sched, data, level, fmt, ## args)
+
+
+#define LOGP_LCHAN_NAME_FMT "TS%u-%s"
+#define LOGP_LCHAN_NAME_ARGS(lchan) \
+ (lchan)->ts->index, l1sched_lchan_desc[(lchan)->type].name
+
+/* Messages using l1sched_lchan_state as the context */
+#define LOGP_LCHAN_CAT(lchan, cat, level, fmt, args...) \
+ LOGP_SCHED_CAT((lchan)->ts->sched, cat, level, LOGP_LCHAN_NAME_FMT " " fmt, \
+ LOGP_LCHAN_NAME_ARGS(lchan), ## args)
+
+/* Common messages using l1sched_lchan_state as the context */
+#define LOGP_LCHANC(lchan, level, fmt, args...) \
+ LOGP_LCHAN_CAT(lchan, common, level, fmt, ## args)
+
+/* Data messages using l1sched_lchan_state as the context */
+#define LOGP_LCHAND(lchan, level, fmt, args...) \
+ LOGP_LCHAN_CAT(lchan, data, level, fmt, ## args)
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/prim.h b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h
new file mode 100644
index 00000000..a9187c2a
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h
@@ -0,0 +1,132 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+
+#define l1sched_prim_from_msgb(msg) \
+ ((struct l1sched_prim *)(msg)->l1h)
+
+#define l1sched_prim_data_from_msgb(msg) \
+ ((uint8_t *)msgb_l2(msg))
+
+#define l1sched_prim_type_from_msgb(msg) \
+ l1sched_prim_from_msgb(msg)->oph.primitive
+
+#define L1SCHED_PRIM_STR_FMT "%s.%s"
+#define L1SCHED_PRIM_STR_ARGS(prim) \
+ l1sched_prim_type_name((prim)->oph.primitive), \
+ osmo_prim_operation_name((prim)->oph.operation)
+
+enum l1sched_prim_type {
+ L1SCHED_PRIM_T_DATA, /* Req | Ind | Cnf */
+ L1SCHED_PRIM_T_RACH, /* Req | Cnf */
+ L1SCHED_PRIM_T_SCH, /* Ind */
+ L1SCHED_PRIM_T_PCHAN_COMB, /* Ind */
+};
+
+extern const struct value_string l1sched_prim_type_names[];
+static inline const char *l1sched_prim_type_name(enum l1sched_prim_type val)
+{
+ return get_value_string(l1sched_prim_type_names, val);
+}
+
+/*! Common header for L1SCHED_PRIM_T_{DATA,RACH} */
+struct l1sched_prim_chdr {
+ /*! TDMA Frame Number */
+ uint32_t frame_nr;
+ /*! RSL Channel Number */
+ uint8_t chan_nr;
+ /*! RSL Link Identifier */
+ uint8_t link_id;
+ /*! Traffic or signalling */
+ bool traffic;
+};
+
+/*! Payload of L1SCHED_PRIM_T_DATA | Ind */
+struct l1sched_prim_data_ind {
+ /*! Common sub-header */
+ struct l1sched_prim_chdr chdr;
+ int16_t toa256;
+ int8_t rssi;
+ int n_errors;
+ int n_bits_total;
+};
+
+/*! Payload of L1SCHED_PRIM_T_RACH | {Req,Cnf} */
+struct l1sched_prim_rach {
+ /*! Common sub-header */
+ struct l1sched_prim_chdr chdr;
+ /*! Training Sequence (only for 11-bit RA) */
+ uint8_t synch_seq;
+ /*! Transmission offset (how many frames to skip) */
+ uint8_t offset;
+ /*! RA value is 11 bit */
+ bool is_11bit;
+ /*! RA value */
+ uint16_t ra;
+};
+
+struct l1sched_prim {
+ /*! Primitive header */
+ struct osmo_prim_hdr oph;
+ /*! Type specific header */
+ union {
+ /*! L1SCHED_PRIM_T_DATA | Req */
+ struct l1sched_prim_chdr data_req;
+ /*! L1SCHED_PRIM_T_DATA | Cnf */
+ struct l1sched_prim_chdr data_cnf;
+ /*! L1SCHED_PRIM_T_DATA | Ind */
+ struct l1sched_prim_data_ind data_ind;
+
+ /*! L1SCHED_PRIM_T_RACH | Req */
+ struct l1sched_prim_rach rach_req;
+ /*! L1SCHED_PRIM_T_RACH | Cnf */
+ struct l1sched_prim_rach rach_cnf;
+
+ /*! L1SCHED_PRIM_T_SCH | Ind */
+ struct {
+ /*! TDMA frame number */
+ uint32_t frame_nr;
+ /*! BSIC */
+ uint8_t bsic;
+ } sch_ind;
+
+ /*! L1SCHED_PRIM_T_PCHAN_COMB | Ind */
+ struct {
+ /*! Timeslot number */
+ uint8_t tn;
+ /*! Channel combination for a timeslot */
+ enum gsm_phys_chan_config pchan;
+ } pchan_comb_ind;
+ };
+};
+
+
+struct l1sched_state;
+struct l1sched_lchan_state;
+
+void l1sched_prim_init(struct msgb *msg,
+ enum l1sched_prim_type type,
+ enum osmo_prim_operation op);
+
+struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
+ enum osmo_prim_operation op);
+
+bool l1sched_lchan_amr_prim_is_valid(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, bool is_cmr);
+struct msgb *l1sched_lchan_prim_dequeue_sacch(struct l1sched_lchan_state *lchan);
+struct msgb *l1sched_lchan_prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch);
+struct msgb *l1sched_lchan_prim_dummy_lapdm(const struct l1sched_lchan_state *lchan);
+
+int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
+ const uint8_t *data, size_t data_len,
+ int n_errors, int n_bits_total, bool traffic);
+int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, uint32_t fn);
+
+int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg);
+int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am b/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am
new file mode 100644
index 00000000..ad1e89a0
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am
@@ -0,0 +1,9 @@
+noinst_HEADERS = \
+ l1ctl_server.h \
+ l1ctl.h \
+ phyif.h \
+ trx_if.h \
+ logging.h \
+ trxcon.h \
+ trxcon_fsm.h \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h
new file mode 100644
index 00000000..7e2fa6a5
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <stdint.h>
+
+struct msgb;
+struct trxcon_param_rx_data_ind;
+struct trxcon_param_tx_data_cnf;
+struct trxcon_param_tx_access_burst_cnf;
+
+int l1ctl_tx_fbsb_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, uint8_t bsic);
+int l1ctl_tx_fbsb_fail(struct trxcon_inst *trxcon, uint16_t band_arfcn);
+int l1ctl_tx_ccch_mode_conf(struct trxcon_inst *trxcon, uint8_t mode);
+int l1ctl_tx_pm_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, int dbm, int last);
+int l1ctl_tx_reset_conf(struct trxcon_inst *trxcon, uint8_t type);
+int l1ctl_tx_reset_ind(struct trxcon_inst *trxcon, uint8_t type);
+
+int l1ctl_tx_dt_ind(struct trxcon_inst *trxcon,
+ const struct trxcon_param_rx_data_ind *ind);
+int l1ctl_tx_dt_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_data_cnf *cnf);
+int l1ctl_tx_rach_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_access_burst_cnf *cnf);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
new file mode 100644
index 00000000..83c61f02
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+
+#define L1CTL_LENGTH 512
+#define L1CTL_HEADROOM 32
+
+/**
+ * Each L1CTL message gets its own length pushed
+ * as two bytes in front before sending.
+ */
+#define L1CTL_MSG_LEN_FIELD 2
+
+struct l1ctl_client;
+
+typedef int l1ctl_conn_data_func(struct l1ctl_client *, struct msgb *);
+typedef void l1ctl_conn_state_func(struct l1ctl_client *);
+
+struct l1ctl_server_cfg {
+ /* UNIX socket path to listen on */
+ const char *sock_path;
+ /* maximum number of connected clients */
+ unsigned int num_clients_max;
+ /* functions to be called on various events */
+ l1ctl_conn_data_func *conn_read_cb; /* mandatory */
+ l1ctl_conn_state_func *conn_accept_cb; /* optional */
+ l1ctl_conn_state_func *conn_close_cb; /* optional */
+};
+
+struct l1ctl_server {
+ /* list of connected clients */
+ struct llist_head clients;
+ /* number of connected clients */
+ unsigned int num_clients;
+ /* used for client ID generation */
+ unsigned int next_client_id;
+ /* socket on which we listen for connections */
+ struct osmo_fd ofd;
+ /* server configuration */
+ const struct l1ctl_server_cfg *cfg;
+};
+
+struct l1ctl_client {
+ /* list head in l1ctl_server.clients */
+ struct llist_head list;
+ /* struct l1ctl_server we belong to */
+ struct l1ctl_server *server;
+ /* client's write queue */
+ struct osmo_wqueue wq;
+ /* logging context (used as prefix for messages) */
+ const char *log_prefix;
+ /* unique client ID */
+ unsigned int id;
+ /* some private data */
+ void *priv;
+};
+
+struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg);
+void l1ctl_server_free(struct l1ctl_server *server);
+
+int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg);
+void l1ctl_client_conn_close(struct l1ctl_client *client);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/logging.h b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h
new file mode 100644
index 00000000..ce149926
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <osmocom/core/logging.h>
+
+enum {
+ DAPP,
+ DL1C,
+ DL1D,
+ DTRXC,
+ DTRXD,
+ DSCH,
+ DSCHD,
+ DGPRS,
+};
+
+int trxcon_logging_init(void *tall_ctx, const char *category_mask);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h b/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h
new file mode 100644
index 00000000..2ad7a678
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/* PHYIF command type */
+enum trxcon_phyif_cmd_type {
+ TRXCON_PHYIF_CMDT_RESET,
+ TRXCON_PHYIF_CMDT_POWERON,
+ TRXCON_PHYIF_CMDT_POWEROFF,
+ TRXCON_PHYIF_CMDT_MEASURE,
+ TRXCON_PHYIF_CMDT_SETFREQ_H0,
+ TRXCON_PHYIF_CMDT_SETFREQ_H1,
+ TRXCON_PHYIF_CMDT_SETSLOT,
+ TRXCON_PHYIF_CMDT_SETTA,
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETFREQ_H0 */
+struct trxcon_phyif_cmdp_setfreq_h0 {
+ uint16_t band_arfcn;
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETFREQ_H1 */
+struct trxcon_phyif_cmdp_setfreq_h1 {
+ uint8_t hsn;
+ uint8_t maio;
+ const uint16_t *ma;
+ unsigned int ma_len;
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETSLOT */
+struct trxcon_phyif_cmdp_setslot {
+ uint8_t tn;
+ uint8_t pchan; /* enum gsm_phys_chan_config */
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETTA */
+struct trxcon_phyif_cmdp_setta {
+ int8_t ta; /* intentionally signed */
+};
+
+/* param of TRXCON_PHYIF_CMDT_MEASURE (command) */
+struct trxcon_phyif_cmdp_measure {
+ uint16_t band_arfcn;
+};
+
+/* param of TRXCON_PHYIF_CMDT_MEASURE (response) */
+struct trxcon_phyif_rspp_measure {
+ uint16_t band_arfcn;
+ int dbm;
+};
+
+struct trxcon_phyif_cmd {
+ enum trxcon_phyif_cmd_type type;
+ union {
+ struct trxcon_phyif_cmdp_setfreq_h0 setfreq_h0;
+ struct trxcon_phyif_cmdp_setfreq_h1 setfreq_h1;
+ struct trxcon_phyif_cmdp_setslot setslot;
+ struct trxcon_phyif_cmdp_setta setta;
+ struct trxcon_phyif_cmdp_measure measure;
+ } param;
+};
+
+struct trxcon_phyif_rsp {
+ enum trxcon_phyif_cmd_type type;
+ union {
+ struct trxcon_phyif_rspp_measure measure;
+ } param;
+};
+
+/* RTS.ind - Ready-to-Send indication */
+struct trxcon_phyif_rts_ind {
+ uint32_t fn;
+ uint8_t tn;
+};
+
+/* RTR.ind - Ready-to-Receive indicaton */
+struct trxcon_phyif_rtr_ind {
+ uint32_t fn;
+ uint8_t tn;
+};
+
+/* The probed lchan is active */
+#define TRXCON_PHYIF_RTR_F_ACTIVE (1 << 0)
+
+/* RTR.rsp - Ready-to-Receive response */
+struct trxcon_phyif_rtr_rsp {
+ uint32_t flags; /* see TRXCON_PHYIF_RTR_F_* above */
+};
+
+/* BURST.req - a burst to be transmitted */
+struct trxcon_phyif_burst_req {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t pwr;
+ const ubit_t *burst;
+ unsigned int burst_len;
+};
+
+/* BURST.ind - a received burst */
+struct trxcon_phyif_burst_ind {
+ uint32_t fn;
+ uint8_t tn;
+ int16_t toa256;
+ int8_t rssi;
+ const sbit_t *burst;
+ unsigned int burst_len;
+};
+
+int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br);
+int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *bi);
+
+int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts);
+int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind,
+ struct trxcon_phyif_rtr_rsp *rsp);
+
+int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd);
+int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp);
+void trxcon_phyif_close(void *phyif);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h b/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h
new file mode 100644
index 00000000..e564fd8e
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/bb/trxcon/phyif.h>
+
+#define TRXC_BUF_SIZE 1024
+#define TRXD_BUF_SIZE 512
+
+enum trx_fsm_states {
+ TRX_STATE_OFFLINE = 0,
+ TRX_STATE_IDLE,
+ TRX_STATE_ACTIVE,
+ TRX_STATE_RSP_WAIT,
+};
+
+struct trx_instance {
+ struct osmo_fd trx_ofd_ctrl;
+ struct osmo_fd trx_ofd_data;
+
+ struct osmo_timer_list trx_ctrl_timer;
+ struct llist_head trx_ctrl_list;
+ struct osmo_fsm_inst *fi;
+ uint32_t fn_advance;
+
+ /* HACK: we need proper state machines */
+ uint32_t prev_state;
+ bool powered_up;
+
+ /* Some private data */
+ void *priv;
+};
+
+struct trx_ctrl_msg {
+ struct llist_head list;
+ char cmd[TRXC_BUF_SIZE];
+ int retry_cnt;
+ int critical;
+ int cmd_len;
+};
+
+struct trx_if_params {
+ const char *local_host;
+ const char *remote_host;
+ uint16_t base_port;
+ uint32_t fn_advance;
+ uint8_t instance;
+
+ struct osmo_fsm_inst *parent_fi;
+ uint32_t parent_term_event;
+ void *priv;
+};
+
+struct trx_instance *trx_if_open(const struct trx_if_params *params);
+void trx_if_close(struct trx_instance *trx);
+
+int trx_if_handle_phyif_burst_req(struct trx_instance *trx, const struct trxcon_phyif_burst_req *br);
+int trx_if_handle_phyif_cmd(struct trx_instance *trx, const struct trxcon_phyif_cmd *cmd);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
new file mode 100644
index 00000000..ff54e785
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <stdint.h>
+
+struct osmo_fsm_inst;
+struct l1sched_state;
+struct l1gprs_state;
+struct msgb;
+
+struct trxcon_inst {
+ struct osmo_fsm_inst *fi;
+ unsigned int id;
+
+ /* Logging context for sched and l1c */
+ const char *log_prefix;
+
+ /* GSMTAP instance (optional) */
+ struct gsmtap_inst *gsmtap;
+
+ /* The L1 scheduler */
+ struct l1sched_state *sched;
+ /* GPRS state (MAC layer) */
+ struct l1gprs_state *gprs;
+
+ /* PHY interface (e.g. TRXC/TRXD) */
+ void *phyif;
+ /* L2 interface (e.g. L1CTL) */
+ void *l2if;
+
+ /* State specific data of trxcon_fsm */
+ void *fi_data;
+
+ /* L1 parameters */
+ struct {
+ uint16_t band_arfcn;
+ uint8_t tx_power;
+ uint8_t tsc; /* only valid for DCCH/PDCH */
+ int8_t ta;
+ } l1p;
+
+ /* PHY specific quirks */
+ struct {
+ /* FBSB timeout extension (in TDMA FNs) */
+ unsigned int fbsb_extend_fns;
+ } phy_quirks;
+};
+
+enum trxcon_log_cat {
+ TRXCON_LOGC_FSM, /* trxcon_fsm */
+ TRXCON_LOGC_L1C, /* L1CTL control */
+ TRXCON_LOGC_L1D, /* L1CTL data */
+ TRXCON_LOGC_SCHC, /* l1sched control */
+ TRXCON_LOGC_SCHD, /* l1sched data */
+ TRXCON_LOGC_GPRS, /* l1gprs logging */
+};
+
+void trxcon_set_log_cfg(const int *logc, unsigned int logc_num);
+
+struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id);
+void trxcon_inst_free(struct trxcon_inst *trxcon);
+
+int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg);
+int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg);
+void trxcon_l1ctl_close(struct trxcon_inst *trxcon);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h
new file mode 100644
index 00000000..9eba4fde
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h
@@ -0,0 +1,169 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+
+extern struct osmo_fsm trxcon_fsm_def;
+
+enum trxcon_fsm_states {
+ TRXCON_ST_RESET,
+ TRXCON_ST_FULL_POWER_SCAN,
+ TRXCON_ST_FBSB_SEARCH,
+ TRXCON_ST_BCCH_CCCH,
+ TRXCON_ST_DEDICATED,
+ TRXCON_ST_PACKET_DATA,
+};
+
+enum trxcon_fsm_events {
+ TRXCON_EV_PHYIF_FAILURE,
+ TRXCON_EV_L2IF_FAILURE,
+ TRXCON_EV_RESET_FULL_REQ,
+ TRXCON_EV_RESET_SCHED_REQ,
+ TRXCON_EV_FULL_POWER_SCAN_REQ,
+ TRXCON_EV_FULL_POWER_SCAN_RES,
+ TRXCON_EV_FBSB_SEARCH_REQ,
+ TRXCON_EV_FBSB_SEARCH_RES,
+ TRXCON_EV_SET_CCCH_MODE_REQ,
+ TRXCON_EV_SET_TCH_MODE_REQ,
+ TRXCON_EV_SET_PHY_CONFIG_REQ,
+ TRXCON_EV_TX_ACCESS_BURST_REQ,
+ TRXCON_EV_TX_ACCESS_BURST_CNF,
+ TRXCON_EV_UPDATE_SACCH_CACHE_REQ,
+ TRXCON_EV_DCH_EST_REQ,
+ TRXCON_EV_DCH_REL_REQ,
+ TRXCON_EV_TX_DATA_REQ,
+ TRXCON_EV_TX_DATA_CNF,
+ TRXCON_EV_RX_DATA_IND,
+ TRXCON_EV_CRYPTO_REQ,
+ TRXCON_EV_GPRS_UL_TBF_CFG_REQ, /* param: L1CTL msgb */
+ TRXCON_EV_GPRS_DL_TBF_CFG_REQ, /* param: L1CTL msgb */
+ TRXCON_EV_GPRS_UL_BLOCK_REQ, /* param: L1CTL msgb */
+};
+
+/* param of TRXCON_EV_FULL_POWER_SCAN_REQ */
+struct trxcon_param_full_power_scan_req {
+ uint16_t band_arfcn_start;
+ uint16_t band_arfcn_stop;
+};
+
+/* param of TRXCON_EV_FULL_POWER_SCAN_RES */
+struct trxcon_param_full_power_scan_res {
+ uint16_t band_arfcn;
+ int dbm;
+};
+
+/* param of TRXCON_EV_FBSB_SEARCH_REQ */
+struct trxcon_param_fbsb_search_req {
+ uint16_t band_arfcn;
+ uint16_t timeout_fns; /* in TDMA Fn periods */
+ uint8_t pchan_config;
+};
+
+/* param of TRXCON_EV_SET_{CCCH,TCH}_MODE_REQ */
+struct trxcon_param_set_ccch_tch_mode_req {
+ uint8_t mode;
+ struct {
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
+ bool applied;
+};
+
+/* param of TRXCON_EV_SET_PHY_CONFIG_REQ */
+struct trxcon_param_set_phy_config_req {
+ enum {
+ TRXCON_PHY_CFGT_PCHAN_COMB,
+ TRXCON_PHY_CFGT_TX_PARAMS,
+ } type;
+ union {
+ struct {
+ uint8_t tn;
+ uint8_t pchan;
+ } pchan_comb;
+ struct {
+ uint8_t timing_advance;
+ uint8_t tx_power;
+ } tx_params;
+ };
+};
+
+/* param of TRXCON_EV_TX_DATA_REQ */
+struct trxcon_param_tx_data_req {
+ bool traffic;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+/* param of TRXCON_EV_TX_DATA_CNF */
+struct trxcon_param_tx_data_cnf {
+ bool traffic;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint16_t band_arfcn;
+ uint32_t frame_nr;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+/* param of TRXCON_EV_RX_DATA_IND */
+struct trxcon_param_rx_data_ind {
+ bool traffic;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint16_t band_arfcn;
+ uint32_t frame_nr;
+ int16_t toa256;
+ int8_t rssi;
+ int n_errors;
+ int n_bits_total;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+/* param of TRXCON_EV_TX_ACCESS_BURST_REQ */
+struct trxcon_param_tx_access_burst_req {
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint8_t offset;
+ uint8_t synch_seq;
+ uint16_t ra;
+ bool is_11bit;
+};
+
+/* param of TRXCON_EV_TX_ACCESS_BURST_CNF */
+struct trxcon_param_tx_access_burst_cnf {
+ uint16_t band_arfcn;
+ uint32_t frame_nr;
+};
+
+/* param of TRXCON_EV_DCH_EST_REQ */
+struct trxcon_param_dch_est_req {
+ uint8_t chan_nr;
+ uint8_t tch_mode;
+ uint8_t tsc;
+
+ bool hopping;
+ union {
+ struct { /* hopping=false */
+ uint16_t band_arfcn;
+ } h0;
+ struct { /* hopping=true */
+ uint8_t hsn;
+ uint8_t maio;
+ uint8_t n;
+ uint16_t ma[64];
+ } h1;
+ };
+};
+
+/* param of TRXCON_EV_CRYPTO_REQ */
+struct trxcon_param_crypto_req {
+ uint8_t chan_nr;
+ uint8_t a5_algo; /* 0 is A5/0 */
+ uint8_t key_len;
+ const uint8_t *key;
+};
diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c
deleted file mode 100644
index 8083595f..00000000
--- a/src/host/trxcon/l1ctl.c
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * GSM L1 control interface handlers
- *
- * (C) 2014 by Sylvain Munaut <tnt@246tNt.com>
- * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * 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
- * (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.
- *
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
-
-#include "logging.h"
-#include "l1ctl_link.h"
-#include "l1ctl_proto.h"
-
-#include "trx_if.h"
-#include "sched_trx.h"
-
-static const char *arfcn2band_name(uint16_t arfcn)
-{
- enum gsm_band band;
-
- if (gsm_arfcn2band_rc(arfcn, &band) < 0)
- return "(invalid)";
-
- return gsm_band_name(band);
-}
-
-static struct msgb *l1ctl_alloc_msg(uint8_t msg_type)
-{
- struct l1ctl_hdr *l1h;
- struct msgb *msg;
-
- /**
- * Each L1CTL message gets its own length pushed in front
- * before sending. This is why we need this small headroom.
- */
- msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_MSG_LEN_FIELD,
- L1CTL_MSG_LEN_FIELD, "l1ctl_tx_msg");
- if (!msg) {
- LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n");
- return NULL;
- }
-
- msg->l1h = msgb_put(msg, sizeof(*l1h));
- l1h = (struct l1ctl_hdr *) msg->l1h;
- l1h->msg_type = msg_type;
-
- return msg;
-}
-
-int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn,
- int dbm, int last)
-{
- struct l1ctl_pm_conf *pmc;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_PM_CONF);
- if (!msg)
- return -ENOMEM;
-
- LOGP(DL1C, LOGL_DEBUG, "Send PM Conf (%s %d = %d dBm)\n",
- arfcn2band_name(band_arfcn),
- band_arfcn &~ ARFCN_FLAG_MASK, dbm);
-
- pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
- pmc->band_arfcn = htons(band_arfcn);
- pmc->pm[0] = dbm2rxlev(dbm);
- pmc->pm[1] = 0;
-
- if (last) {
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h;
- l1h->flags |= L1CTL_F_DONE;
- }
-
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type)
-{
- struct msgb *msg;
- struct l1ctl_reset *res;
-
- msg = l1ctl_alloc_msg(L1CTL_RESET_IND);
- if (!msg)
- return -ENOMEM;
-
- LOGP(DL1C, LOGL_DEBUG, "Send Reset Ind (%u)\n", type);
-
- res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
- res->type = type;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type)
-{
- struct msgb *msg;
- struct l1ctl_reset *res;
-
- msg = l1ctl_alloc_msg(L1CTL_RESET_CONF);
- if (!msg)
- return -ENOMEM;
-
- LOGP(DL1C, LOGL_DEBUG, "Send Reset Conf (%u)\n", type);
- res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
- res->type = type;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static struct l1ctl_info_dl *put_dl_info_hdr(struct msgb *msg, struct l1ctl_info_dl *dl_info)
-{
- size_t len = sizeof(struct l1ctl_info_dl);
- struct l1ctl_info_dl *dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
-
- if (dl_info) /* Copy DL info provided by handler */
- memcpy(dl, dl_info, len);
- else /* Init DL info header */
- memset(dl, 0x00, len);
-
- return dl;
-}
-
-/* Fill in FBSB payload: BSIC and sync result */
-static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result, uint8_t bsic)
-{
- struct l1ctl_fbsb_conf *conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf));
-
- LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=%u, bsic=%u)\n", result, bsic);
-
- conf->result = result;
- conf->bsic = bsic;
-
- return conf;
-}
-
-int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result,
- struct l1ctl_info_dl *dl_info, uint8_t bsic)
-{
- struct l1ctl_fbsb_conf *conf;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- put_dl_info_hdr(msg, dl_info);
- talloc_free(dl_info);
-
- conf = fbsb_conf_make(msg, result, bsic);
-
- /* FIXME: set proper value */
- conf->initial_freq_err = 0;
-
- /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
- l1l->fbsb_conf_sent = true;
-
- /* Abort FBSB expire timer */
- if (osmo_timer_pending(&l1l->fbsb_timer))
- osmo_timer_del(&l1l->fbsb_timer);
-
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode)
-{
- struct l1ctl_ccch_mode_conf *conf;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf));
- conf->ccch_mode = mode;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-/**
- * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND.
- */
-int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
- uint8_t *l2, size_t l2_len, bool traffic)
-{
- struct msgb *msg;
- uint8_t *msg_l2;
-
- msg = l1ctl_alloc_msg(traffic ?
- L1CTL_TRAFFIC_IND : L1CTL_DATA_IND);
- if (msg == NULL)
- return -ENOMEM;
-
- put_dl_info_hdr(msg, data);
-
- /* Copy the L2 payload if preset */
- if (l2 && l2_len > 0) {
- msg_l2 = (uint8_t *) msgb_put(msg, l2_len);
- memcpy(msg_l2, l2, l2_len);
- }
-
- /* Put message to upper layers */
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_rach_conf(struct l1ctl_link *l1l,
- uint16_t band_arfcn, uint32_t fn)
-{
- struct l1ctl_info_dl *dl;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_RACH_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- dl = put_dl_info_hdr(msg, NULL);
- memset(dl, 0x00, sizeof(*dl));
-
- dl->band_arfcn = htons(band_arfcn);
- dl->frame_nr = htonl(fn);
-
- return l1ctl_link_send(l1l, msg);
-}
-
-
-/**
- * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF.
- */
-int l1ctl_tx_dt_conf(struct l1ctl_link *l1l,
- struct l1ctl_info_dl *data, bool traffic)
-{
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(traffic ?
- L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- /* Copy DL frame header from source message */
- put_dl_info_hdr(msg, data);
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mode)
-{
- switch (mode) {
- /* TODO: distinguish extended BCCH */
- case CCCH_MODE_NON_COMBINED:
- case CCCH_MODE_NONE:
- return GSM_PCHAN_CCCH;
-
- case CCCH_MODE_COMBINED:
- return GSM_PCHAN_CCCH_SDCCH4;
- case CCCH_MODE_COMBINED_CBCH:
- return GSM_PCHAN_CCCH_SDCCH4_CBCH;
-
- default:
- LOGP(DL1C, LOGL_NOTICE, "Undandled CCCH mode (%u), "
- "assuming non-combined configuration\n", mode);
- return GSM_PCHAN_CCCH;
- }
-}
-
-/* FBSB expire timer */
-static void fbsb_timer_cb(void *data)
-{
- struct l1ctl_link *l1l = (struct l1ctl_link *) data;
- struct l1ctl_info_dl *dl;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
- if (msg == NULL)
- return;
-
- LOGP(DL1C, LOGL_NOTICE, "FBSB timer fired for ARFCN %u\n", l1l->trx->band_arfcn &~ ARFCN_FLAG_MASK);
-
- dl = put_dl_info_hdr(msg, NULL);
-
- /* Fill in current ARFCN */
- dl->band_arfcn = htons(l1l->trx->band_arfcn);
-
- fbsb_conf_make(msg, 255, 0);
-
- /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
- l1l->fbsb_conf_sent = true;
-
- l1ctl_link_send(l1l, msg);
-}
-
-static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- enum gsm_phys_chan_config ch_config;
- struct l1ctl_fbsb_req *fbsb;
- uint16_t band_arfcn;
- uint16_t timeout;
- int rc = 0;
-
- fbsb = (struct l1ctl_fbsb_req *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*fbsb)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- ch_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode);
- band_arfcn = ntohs(fbsb->band_arfcn);
- timeout = ntohs(fbsb->timeout);
-
- LOGP(DL1C, LOGL_NOTICE, "Received FBSB request (%s %d)\n",
- arfcn2band_name(band_arfcn),
- band_arfcn &~ ARFCN_FLAG_MASK);
-
- /* Reset scheduler and clock counter */
- sched_trx_reset(l1l->trx, true);
-
- /* Configure a single timeslot */
- sched_trx_configure_ts(l1l->trx, 0, ch_config);
-
- /* Ask SCH handler to send L1CTL_FBSB_CONF */
- l1l->fbsb_conf_sent = false;
-
- /* Only if current ARFCN differs */
- if (l1l->trx->band_arfcn != band_arfcn) {
- /* Update current ARFCN */
- l1l->trx->band_arfcn = band_arfcn;
-
- /* Tune transceiver to required ARFCN */
- trx_if_cmd_rxtune(l1l->trx, band_arfcn);
- trx_if_cmd_txtune(l1l->trx, band_arfcn);
- }
-
- /* Transceiver might have been powered on before, e.g.
- * in case of sending L1CTL_FBSB_REQ due to signal loss. */
- if (!l1l->trx->powered_up)
- trx_if_cmd_poweron(l1l->trx);
-
- /* Start FBSB expire timer */
- l1l->fbsb_timer.data = l1l;
- l1l->fbsb_timer.cb = fbsb_timer_cb;
- LOGP(DL1C, LOGL_INFO, "Starting FBSB timer %u ms\n", timeout * GSM_TDMA_FN_DURATION_uS / 1000);
- osmo_timer_schedule(&l1l->fbsb_timer, 0,
- timeout * GSM_TDMA_FN_DURATION_uS);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- uint16_t band_arfcn_start, band_arfcn_stop;
- struct l1ctl_pm_req *pmr;
- int rc = 0;
-
- pmr = (struct l1ctl_pm_req *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*pmr)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short PM Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- band_arfcn_start = ntohs(pmr->range.band_arfcn_from);
- band_arfcn_stop = ntohs(pmr->range.band_arfcn_to);
-
- LOGP(DL1C, LOGL_NOTICE, "Received power measurement "
- "request (%s: %d -> %d)\n",
- arfcn2band_name(band_arfcn_start),
- band_arfcn_start &~ ARFCN_FLAG_MASK,
- band_arfcn_stop &~ ARFCN_FLAG_MASK);
-
- /* Send measurement request to transceiver */
- rc = trx_if_cmd_measure(l1l->trx, band_arfcn_start, band_arfcn_stop);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_reset *res;
- int rc = 0;
-
- res = (struct l1ctl_reset *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*res)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- LOGP(DL1C, LOGL_NOTICE, "Received reset request (%u)\n",
- res->type);
-
- switch (res->type) {
- case L1CTL_RES_T_FULL:
- /* TODO: implement trx_if_reset() */
- trx_if_cmd_poweroff(l1l->trx);
- trx_if_cmd_echo(l1l->trx);
-
- /* Fall through */
- case L1CTL_RES_T_SCHED:
- sched_trx_reset(l1l->trx, true);
- break;
- default:
- LOGP(DL1C, LOGL_ERROR, "Unknown L1CTL_RESET_REQ type\n");
- goto exit;
- }
-
- /* Confirm */
- rc = l1ctl_tx_reset_conf(l1l, res->type);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_echo_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_hdr *l1h;
-
- LOGP(DL1C, LOGL_NOTICE, "Recv Echo Req\n");
- LOGP(DL1C, LOGL_NOTICE, "Send Echo Conf\n");
-
- /* Nothing to do, just send it back */
- l1h = (struct l1ctl_hdr *) msg->l1h;
- l1h->msg_type = L1CTL_ECHO_CONF;
- msg->data = msg->l1h;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- enum gsm_phys_chan_config ch_config;
- struct l1ctl_ccch_mode_req *req;
- struct trx_ts *ts;
- int rc = 0;
-
- req = (struct l1ctl_ccch_mode_req *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*req)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- LOGP(DL1C, LOGL_NOTICE, "Received CCCH mode request (%u)\n",
- req->ccch_mode); /* TODO: add value-string for ccch_mode */
-
- /* Make sure that TS0 is allocated and configured */
- ts = l1l->trx->ts_list[0];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DL1C, LOGL_ERROR, "TS0 is not configured");
- rc = -EINVAL;
- goto exit;
- }
-
- /* Choose corresponding channel combination */
- ch_config = l1ctl_ccch_mode2pchan_config(req->ccch_mode);
-
- /* Do nothing if the current mode matches required */
- if (ts->mf_layout->chan_config != ch_config)
- rc = sched_trx_configure_ts(l1l->trx, 0, ch_config);
-
- /* Confirm reconfiguration */
- if (!rc)
- rc = l1ctl_tx_ccch_mode_conf(l1l, req->ccch_mode);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg, bool ext)
-{
- struct l1ctl_ext_rach_req *ext_req;
- struct l1ctl_rach_req *req;
- struct l1ctl_info_ul *ul;
- struct trx_ts_prim *prim;
- size_t len;
- int rc;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
-
- /* Is it extended (11-bit) RACH or not? */
- if (ext) {
- ext_req = (struct l1ctl_ext_rach_req *) ul->payload;
- ext_req->offset = ntohs(ext_req->offset);
- ext_req->ra11 = ntohs(ext_req->ra11);
- len = sizeof(*ext_req);
-
- LOGP(DL1C, LOGL_NOTICE, "Received extended (11-bit) RACH request "
- "(offset=%u, synch_seq=%u, ra11=0x%02hx)\n",
- ext_req->offset, ext_req->synch_seq, ext_req->ra11);
- } else {
- req = (struct l1ctl_rach_req *) ul->payload;
- req->offset = ntohs(req->offset);
- len = sizeof(*req);
-
- LOGP(DL1C, LOGL_NOTICE, "Received regular (8-bit) RACH request "
- "(offset=%u, ra=0x%02x)\n", req->offset, req->ra);
- }
-
- /* The controlling L1CTL side always does include the UL info header,
- * but may leave it empty. We assume RACH is on TS0 in this case. */
- if (ul->chan_nr == 0x00) {
- LOGP(DL1C, LOGL_NOTICE, "The UL info header is empty, "
- "assuming RACH is on TS0\n");
- ul->chan_nr = RSL_CHAN_RACH;
- }
-
- /* Init a new primitive */
- rc = sched_prim_init(l1l->trx, &prim, len, ul->chan_nr, ul->link_id);
- if (rc)
- goto exit;
-
- /**
- * Push this primitive to the transmit queue.
- * Indicated timeslot needs to be configured.
- */
- rc = sched_prim_push(l1l->trx, prim, ul->chan_nr);
- if (rc) {
- talloc_free(prim);
- goto exit;
- }
-
- /* Fill in the payload */
- memcpy(prim->payload, ul->payload, len);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_proc_est_req_h0(struct trx_instance *trx, struct l1ctl_h0 *h)
-{
- uint16_t band_arfcn;
- int rc = 0;
-
- band_arfcn = ntohs(h->band_arfcn);
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a single "
- "ARFCN=%u channel\n", band_arfcn &~ ARFCN_FLAG_MASK);
-
- /* Do we need to retune? */
- if (trx->band_arfcn == band_arfcn)
- return 0;
-
- /* Tune transceiver to required ARFCN */
- rc |= trx_if_cmd_rxtune(trx, band_arfcn);
- rc |= trx_if_cmd_txtune(trx, band_arfcn);
- if (rc)
- return rc;
-
- /* Update current ARFCN */
- trx->band_arfcn = band_arfcn;
-
- return 0;
-}
-
-static int l1ctl_proc_est_req_h1(struct trx_instance *trx, struct l1ctl_h1 *h)
-{
- uint16_t ma[64];
- int i, rc;
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a Frequency "
- "Hopping (hsn=%u, maio=%u, chans=%u) channel\n",
- h->hsn, h->maio, h->n);
-
- /* No channels?!? */
- if (!h->n) {
- LOGP(DL1C, LOGL_ERROR, "No channels in mobile allocation?!?\n");
- return -EINVAL;
- } else if (h->n > ARRAY_SIZE(ma)) {
- LOGP(DL1C, LOGL_ERROR, "More than 64 channels in mobile allocation?!?\n");
- return -EINVAL;
- }
-
- /* Convert from network to host byte order */
- for (i = 0; i < h->n; i++)
- ma[i] = ntohs(h->ma[i]);
-
- /* Forward hopping parameters to TRX */
- rc = trx_if_cmd_setfh(trx, h->hsn, h->maio, ma, h->n);
- if (rc)
- return rc;
-
- /**
- * TODO: update the state of trx_instance somehow
- * in order to indicate that it is in hopping mode...
- */
- return 0;
-}
-
-static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- enum gsm_phys_chan_config config;
- struct l1ctl_dm_est_req *est_req;
- struct l1ctl_info_ul *ul;
- struct trx_ts *ts;
- uint8_t chan_nr, tn;
- int rc;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
- est_req = (struct l1ctl_dm_est_req *) ul->payload;
-
- chan_nr = ul->chan_nr;
- tn = chan_nr & 0x07;
-
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_EST_REQ "
- "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n",
- tn, chan_nr, est_req->tsc, est_req->tch_mode);
-
- /* Determine channel config */
- config = sched_trx_chan_nr2pchan_config(chan_nr);
- if (config == GSM_PCHAN_NONE) {
- LOGP(DL1C, LOGL_ERROR, "Couldn't determine channel config\n");
- rc = -EINVAL;
- goto exit;
- }
-
- /* Frequency hopping? */
- if (est_req->h)
- rc = l1ctl_proc_est_req_h1(l1l->trx, &est_req->h1);
- else /* Single ARFCN */
- rc = l1ctl_proc_est_req_h0(l1l->trx, &est_req->h0);
- if (rc)
- goto exit;
-
- /* Update TSC (Training Sequence Code) */
- l1l->trx->tsc = est_req->tsc;
-
- /* Configure requested TS */
- rc = sched_trx_configure_ts(l1l->trx, tn, config);
- ts = l1l->trx->ts_list[tn];
- if (rc) {
- rc = -EINVAL;
- goto exit;
- }
-
- /* Deactivate all lchans */
- sched_trx_deactivate_all_lchans(ts);
-
- /* Activate only requested lchans */
- rc = sched_trx_set_lchans(ts, chan_nr, 1, est_req->tch_mode);
- if (rc) {
- LOGP(DL1C, LOGL_ERROR, "Couldn't activate requested lchans\n");
- rc = -EINVAL;
- goto exit;
- }
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ, "
- "switching back to CCCH\n");
-
- /* Reset scheduler */
- sched_trx_reset(l1l->trx, false);
-
- msgb_free(msg);
- return 0;
-}
-
-/**
- * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ.
- */
-static int l1ctl_rx_dt_req(struct l1ctl_link *l1l,
- struct msgb *msg, bool traffic)
-{
- struct l1ctl_info_ul *ul;
- struct trx_ts_prim *prim;
- uint8_t chan_nr, link_id;
- size_t payload_len;
- int rc;
-
- /* Extract UL frame header */
- ul = (struct l1ctl_info_ul *) msg->l1h;
-
- /* Calculate the payload len */
- msg->l2h = ul->payload;
- payload_len = msgb_l2len(msg);
-
- /* Obtain channel description */
- chan_nr = ul->chan_nr;
- link_id = ul->link_id & 0x40;
-
- LOGP(DL1D, LOGL_DEBUG, "Recv %s Req (chan_nr=0x%02x, "
- "link_id=0x%02x, len=%zu)\n", traffic ? "TRAFFIC" : "DATA",
- chan_nr, link_id, payload_len);
-
- /* Init a new primitive */
- rc = sched_prim_init(l1l->trx, &prim, payload_len,
- chan_nr, link_id);
- if (rc)
- goto exit;
-
- /* Push this primitive to transmit queue */
- rc = sched_prim_push(l1l->trx, prim, chan_nr);
- if (rc) {
- talloc_free(prim);
- goto exit;
- }
-
- /* Fill in the payload */
- memcpy(prim->payload, ul->payload, payload_len);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_par_req *par_req;
- struct l1ctl_info_ul *ul;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
- par_req = (struct l1ctl_par_req *) ul->payload;
-
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_PARAM_REQ "
- "(ta=%d, tx_power=%u)\n", par_req->ta, par_req->tx_power);
-
- /* Instruct TRX to use new TA value */
- if (l1l->trx->ta != par_req->ta) {
- trx_if_cmd_setta(l1l->trx, par_req->ta);
- l1l->trx->ta = par_req->ta;
- }
-
- l1l->trx->tx_power = par_req->tx_power;
-
- msgb_free(msg);
- return 0;
-}
-
-static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_tch_mode_req *req;
- struct trx_lchan_state *lchan;
- struct trx_ts *ts;
- int i;
-
- req = (struct l1ctl_tch_mode_req *) msg->l1h;
-
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_TCH_MODE_REQ "
- "(tch_mode=%u, audio_mode=%u)\n", req->tch_mode, req->audio_mode);
-
- /* Iterate over timeslot list */
- for (i = 0; i < TRX_TS_COUNT; i++) {
- /* Timeslot is not allocated */
- ts = l1l->trx->ts_list[i];
- if (ts == NULL)
- continue;
-
- /* Timeslot is not configured */
- if (ts->mf_layout == NULL)
- continue;
-
- /* Iterate over all allocated lchans */
- llist_for_each_entry(lchan, &ts->lchans, list) {
- /* Omit inactive channels */
- if (!lchan->active)
- continue;
-
- /* Set TCH mode */
- lchan->tch_mode = req->tch_mode;
- }
- }
-
- /* TODO: do we need to care about audio_mode? */
-
- /* Re-use the original message as confirmation */
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
- l1h->msg_type = L1CTL_TCH_MODE_CONF;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static int l1ctl_rx_crypto_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_crypto_req *req;
- struct l1ctl_info_ul *ul;
- struct trx_ts *ts;
- uint8_t tn;
- int rc = 0;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
- req = (struct l1ctl_crypto_req *) ul->payload;
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n",
- req->algo, req->key_len);
-
- /* Determine TS index */
- tn = ul->chan_nr & 0x7;
-
- /* Make sure that required TS is allocated and configured */
- ts = l1l->trx->ts_list[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DL1C, LOGL_ERROR, "TS %u is not configured\n", tn);
- rc = -EINVAL;
- goto exit;
- }
-
- /* Poke scheduler */
- rc = sched_trx_start_ciphering(ts, req->algo, req->key, req->key_len);
- if (rc) {
- LOGP(DL1C, LOGL_ERROR, "Couldn't configure ciphering\n");
- rc = -EINVAL;
- goto exit;
- }
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_hdr *l1h;
-
- l1h = (struct l1ctl_hdr *) msg->l1h;
- msg->l1h = l1h->data;
-
- switch (l1h->msg_type) {
- case L1CTL_FBSB_REQ:
- return l1ctl_rx_fbsb_req(l1l, msg);
- case L1CTL_PM_REQ:
- return l1ctl_rx_pm_req(l1l, msg);
- case L1CTL_RESET_REQ:
- return l1ctl_rx_reset_req(l1l, msg);
- case L1CTL_ECHO_REQ:
- return l1ctl_rx_echo_req(l1l, msg);
- case L1CTL_CCCH_MODE_REQ:
- return l1ctl_rx_ccch_mode_req(l1l, msg);
- case L1CTL_RACH_REQ:
- return l1ctl_rx_rach_req(l1l, msg, false);
- case L1CTL_EXT_RACH_REQ:
- return l1ctl_rx_rach_req(l1l, msg, true);
- case L1CTL_DM_EST_REQ:
- return l1ctl_rx_dm_est_req(l1l, msg);
- case L1CTL_DM_REL_REQ:
- return l1ctl_rx_dm_rel_req(l1l, msg);
- case L1CTL_DATA_REQ:
- return l1ctl_rx_dt_req(l1l, msg, false);
- case L1CTL_TRAFFIC_REQ:
- return l1ctl_rx_dt_req(l1l, msg, true);
- case L1CTL_PARAM_REQ:
- return l1ctl_rx_param_req(l1l, msg);
- case L1CTL_TCH_MODE_REQ:
- return l1ctl_rx_tch_mode_req(l1l, msg);
- case L1CTL_CRYPTO_REQ:
- return l1ctl_rx_crypto_req(l1l, msg);
-
- /* Not (yet) handled messages */
- case L1CTL_NEIGH_PM_REQ:
- case L1CTL_DATA_TBF_REQ:
- case L1CTL_TBF_CFG_REQ:
- case L1CTL_DM_FREQ_REQ:
- case L1CTL_SIM_REQ:
- LOGP(DL1C, LOGL_NOTICE, "Ignoring unsupported message "
- "(type=%u)\n", l1h->msg_type);
- msgb_free(msg);
- return -ENOTSUP;
- default:
- LOGP(DL1C, LOGL_ERROR, "Unknown MSG type %u: %s\n", l1h->msg_type,
- osmo_hexdump(msgb_data(msg), msgb_length(msg)));
- msgb_free(msg);
- return -EINVAL;
- }
-}
-
-void l1ctl_shutdown_cb(struct l1ctl_link *l1l)
-{
- /* Abort FBSB expire timer */
- if (osmo_timer_pending(&l1l->fbsb_timer))
- osmo_timer_del(&l1l->fbsb_timer);
-}
diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h
deleted file mode 100644
index 48bbe097..00000000
--- a/src/host/trxcon/l1ctl.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <osmocom/core/msgb.h>
-
-#include "l1ctl_link.h"
-#include "l1ctl_proto.h"
-
-/* Event handlers */
-int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg);
-void l1ctl_shutdown_cb(struct l1ctl_link *l1l);
-
-int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result,
- struct l1ctl_info_dl *dl_info, uint8_t bsic);
-int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode);
-int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn,
- int dbm, int last);
-int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type);
-int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type);
-
-int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
- uint8_t *l2, size_t l2_len, bool traffic);
-int l1ctl_tx_dt_conf(struct l1ctl_link *l1l,
- struct l1ctl_info_dl *data, bool traffic);
-int l1ctl_tx_rach_conf(struct l1ctl_link *l1l,
- uint16_t band_arfcn, uint32_t fn);
diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c
deleted file mode 100644
index 4c406d6c..00000000
--- a/src/host/trxcon/l1ctl_link.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * GSM L1 control socket (/tmp/osmocom_l2) handlers
- *
- * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
- * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * 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
- * (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.
- *
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include <sys/un.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/write_queue.h>
-
-#include "trxcon.h"
-#include "logging.h"
-#include "l1ctl_link.h"
-#include "l1ctl.h"
-
-static struct value_string l1ctl_evt_names[] = {
- { 0, NULL } /* no events? */
-};
-
-static struct osmo_fsm_state l1ctl_fsm_states[] = {
- [L1CTL_STATE_IDLE] = {
- .out_state_mask = GEN_MASK(L1CTL_STATE_CONNECTED),
- .name = "IDLE",
- },
- [L1CTL_STATE_CONNECTED] = {
- .out_state_mask = GEN_MASK(L1CTL_STATE_IDLE),
- .name = "CONNECTED",
- },
-};
-
-static struct osmo_fsm l1ctl_fsm = {
- .name = "l1ctl_link_fsm",
- .states = l1ctl_fsm_states,
- .num_states = ARRAY_SIZE(l1ctl_fsm_states),
- .log_subsys = DL1C,
- .event_names = l1ctl_evt_names,
-};
-
-static int l1ctl_link_read_cb(struct osmo_fd *bfd)
-{
- struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data;
- struct msgb *msg;
- uint16_t len;
- int rc;
-
- /* Attempt to read from socket */
- rc = read(bfd->fd, &len, L1CTL_MSG_LEN_FIELD);
- if (rc < L1CTL_MSG_LEN_FIELD) {
- LOGP(DL1D, LOGL_NOTICE, "L1CTL has lost connection\n");
- if (rc >= 0)
- rc = -EIO;
- l1ctl_link_close_conn(l1l);
- return rc;
- }
-
- /* Check message length */
- len = ntohs(len);
- if (len > L1CTL_LENGTH) {
- LOGP(DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
- return -EINVAL;
- }
-
- /* Allocate a new msg */
- msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
- L1CTL_HEADROOM, "l1ctl_rx_msg");
- if (!msg) {
- LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n");
- return -ENOMEM;
- }
-
- msg->l1h = msgb_put(msg, len);
- rc = read(bfd->fd, msg->l1h, msgb_l1len(msg));
- if (rc != len) {
- LOGP(DL1D, LOGL_ERROR, "Can not read data: len=%d < rc=%d: "
- "%s\n", len, rc, strerror(errno));
- msgb_free(msg);
- return rc;
- }
-
- /* Debug print */
- LOGP(DL1D, LOGL_DEBUG, "RX: '%s'\n",
- osmo_hexdump(msg->data, msg->len));
-
- /* Call L1CTL handler */
- l1ctl_rx_cb(l1l, msg);
-
- return 0;
-}
-
-static int l1ctl_link_write_cb(struct osmo_fd *bfd, struct msgb *msg)
-{
- int len;
-
- if (bfd->fd <= 0)
- return -EINVAL;
-
- len = write(bfd->fd, msg->data, msg->len);
- if (len != msg->len) {
- LOGP(DL1D, LOGL_ERROR, "Failed to write data: "
- "written (%d) < msg_len (%d)\n", len, msg->len);
- return -1;
- }
-
- return 0;
-}
-
-/* Connection handler */
-static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags)
-{
- struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data;
- struct osmo_fd *conn_bfd = &l1l->wq.bfd;
- struct sockaddr_un un_addr;
- socklen_t len;
- int cfd;
-
- len = sizeof(un_addr);
- cfd = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
- if (cfd < 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to accept a new connection\n");
- return -1;
- }
-
- /* Check if we already have an active connection */
- if (conn_bfd->fd != -1) {
- LOGP(DL1C, LOGL_NOTICE, "A new connection rejected: "
- "we already have another active\n");
- close(cfd);
- return 0;
- }
-
- osmo_wqueue_init(&l1l->wq, 100);
- INIT_LLIST_HEAD(&conn_bfd->list);
-
- l1l->wq.write_cb = l1ctl_link_write_cb;
- l1l->wq.read_cb = l1ctl_link_read_cb;
- osmo_fd_setup(conn_bfd, cfd, OSMO_FD_READ, osmo_wqueue_bfd_cb, l1l, 0);
-
- if (osmo_fd_register(conn_bfd) != 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to register new connection fd\n");
- close(conn_bfd->fd);
- conn_bfd->fd = -1;
- return -1;
- }
-
- osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_CONNECT, l1l);
- osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_CONNECTED, 0, 0);
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL has a new connection\n");
-
- return 0;
-}
-
-int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg)
-{
- uint8_t *len;
-
- /* Debug print */
- LOGP(DL1D, LOGL_DEBUG, "TX: '%s'\n",
- osmo_hexdump(msg->data, msg->len));
-
- if (msg->l1h != msg->data)
- LOGP(DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
-
- /* Prepend 16-bit length before sending */
- len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
- osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
-
- if (osmo_wqueue_enqueue(&l1l->wq, msg) != 0) {
- LOGP(DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
- msgb_free(msg);
- return -EIO;
- }
-
- return 0;
-}
-
-int l1ctl_link_close_conn(struct l1ctl_link *l1l)
-{
- struct osmo_fd *conn_bfd = &l1l->wq.bfd;
-
- if (conn_bfd->fd <= 0)
- return -EINVAL;
-
- /* Close connection socket */
- osmo_fd_unregister(conn_bfd);
- close(conn_bfd->fd);
- conn_bfd->fd = -1;
-
- /* Clear pending messages */
- osmo_wqueue_clear(&l1l->wq);
-
- osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_DISCONNECT, l1l);
- osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_IDLE, 0, 0);
-
- return 0;
-}
-
-struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path)
-{
- struct l1ctl_link *l1l;
- struct osmo_fd *bfd;
- int rc;
-
- LOGP(DL1C, LOGL_NOTICE, "Init L1CTL link (%s)\n", sock_path);
-
- l1l = talloc_zero(tall_ctx, struct l1ctl_link);
- if (!l1l) {
- LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n");
- return NULL;
- }
-
- /* Allocate a new dedicated state machine */
- l1l->fsm = osmo_fsm_inst_alloc(&l1ctl_fsm, l1l,
- NULL, LOGL_DEBUG, "l1ctl_link");
- if (l1l->fsm == NULL) {
- LOGP(DTRX, LOGL_ERROR, "Failed to allocate an instance "
- "of FSM '%s'\n", l1ctl_fsm.name);
- talloc_free(l1l);
- return NULL;
- }
-
- /* Create a socket and bind handlers */
- bfd = &l1l->listen_bfd;
-
- /* Bind connection handler */
- osmo_fd_setup(bfd, -1, OSMO_FD_READ, l1ctl_link_accept, l1l, 0);
-
- rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path,
- OSMO_SOCK_F_BIND);
- if (rc < 0) {
- LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n",
- strerror(errno));
- osmo_fsm_inst_free(l1l->fsm);
- talloc_free(l1l);
- return NULL;
- }
-
- /* Bind shutdown handler */
- l1l->shutdown_cb = l1ctl_shutdown_cb;
-
- /**
- * To be able to accept first connection and
- * drop others, it should be set to -1
- */
- l1l->wq.bfd.fd = -1;
-
- return l1l;
-}
-
-void l1ctl_link_shutdown(struct l1ctl_link *l1l)
-{
- struct osmo_fd *listen_bfd;
-
- /* May be unallocated due to init error */
- if (!l1l)
- return;
-
- LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL link\n");
-
- /* Call shutdown callback */
- if (l1l->shutdown_cb != NULL)
- l1l->shutdown_cb(l1l);
-
- listen_bfd = &l1l->listen_bfd;
-
- /* Check if we have an established connection */
- if (l1l->wq.bfd.fd != -1)
- l1ctl_link_close_conn(l1l);
-
- /* Unbind listening socket */
- if (listen_bfd->fd != -1) {
- osmo_fd_unregister(listen_bfd);
- close(listen_bfd->fd);
- listen_bfd->fd = -1;
- }
-
- osmo_fsm_inst_free(l1l->fsm);
- talloc_free(l1l);
-}
-
-static __attribute__((constructor)) void on_dso_load(void)
-{
- OSMO_ASSERT(osmo_fsm_register(&l1ctl_fsm) == 0);
-}
diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h
deleted file mode 100644
index a333e407..00000000
--- a/src/host/trxcon/l1ctl_link.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/fsm.h>
-
-#define L1CTL_LENGTH 256
-#define L1CTL_HEADROOM 32
-
-/**
- * Each L1CTL message gets its own length pushed
- * as two bytes in front before sending.
- */
-#define L1CTL_MSG_LEN_FIELD 2
-
-/* Forward declaration to avoid mutual include */
-struct trx_instance;
-
-enum l1ctl_fsm_states {
- L1CTL_STATE_IDLE = 0,
- L1CTL_STATE_CONNECTED,
-};
-
-struct l1ctl_link {
- struct osmo_fsm_inst *fsm;
- struct osmo_fd listen_bfd;
- struct osmo_wqueue wq;
-
- /* Bind TRX instance */
- struct trx_instance *trx;
-
- /* L1CTL handlers specific */
- struct osmo_timer_list fbsb_timer;
- bool fbsb_conf_sent;
-
- /* Shutdown callback */
- void (*shutdown_cb)(struct l1ctl_link *l1l);
-};
-
-struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path);
-void l1ctl_link_shutdown(struct l1ctl_link *l1l);
-
-int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg);
-int l1ctl_link_close_conn(struct l1ctl_link *l1l);
diff --git a/src/host/trxcon/l1ctl_proto.h b/src/host/trxcon/l1ctl_proto.h
deleted file mode 120000
index 75862bae..00000000
--- a/src/host/trxcon/l1ctl_proto.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../include/l1ctl_proto.h \ No newline at end of file
diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h
deleted file mode 100644
index 152c3467..00000000
--- a/src/host/trxcon/logging.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <osmocom/core/logging.h>
-
-#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH:DSCHD"
-
-enum {
- DAPP,
- DL1C,
- DL1D,
- DTRX,
- DTRXD,
- DSCH,
- DSCHD,
-};
-
-int trx_log_init(void *tall_ctx, const char *category_mask);
diff --git a/src/host/trxcon/m4/.gitkeep b/src/host/trxcon/m4/.gitkeep
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/host/trxcon/m4/.gitkeep
diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c
deleted file mode 100644
index 9476ccd7..00000000
--- a/src/host/trxcon/sched_clck.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: clock synchronization
- *
- * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
- * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
- * (C) 2015 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 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 <errno.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <string.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/bits.h>
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/timer_compat.h>
-#include <osmocom/gsm/a5.h>
-
-#include "scheduler.h"
-#include "logging.h"
-#include "trx_if.h"
-
-#define MAX_FN_SKEW 50
-#define TRX_LOSS_FRAMES 400
-
-static void sched_clck_tick(void *data)
-{
- struct trx_sched *sched = (struct trx_sched *) data;
- struct timespec tv_now, *tv_clock, elapsed;
- int64_t elapsed_us;
- const struct timespec frame_duration = { .tv_sec = 0, .tv_nsec = GSM_TDMA_FN_DURATION_nS };
-
- /* Check if transceiver is still alive */
- if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) {
- LOGP(DSCH, LOGL_DEBUG, "No more clock from transceiver\n");
- sched->state = SCH_CLCK_STATE_WAIT;
-
- return;
- }
-
- /* Get actual / previous frame time */
- osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
- tv_clock = &sched->clock;
-
- timespecsub(&tv_now, tv_clock, &elapsed);
- elapsed_us = (elapsed.tv_sec * 1000000) + (elapsed.tv_nsec / 1000);
-
- /* If someone played with clock, or if the process stalled */
- if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) {
- LOGP(DSCH, LOGL_NOTICE, "PC clock skew: "
- "elapsed uS %" PRId64 "\n", elapsed_us);
-
- sched->state = SCH_CLCK_STATE_WAIT;
-
- return;
- }
-
- /* Schedule next FN clock */
- while (elapsed_us > GSM_TDMA_FN_DURATION_uS / 2) {
- timespecadd(tv_clock, &frame_duration, tv_clock);
- elapsed_us -= GSM_TDMA_FN_DURATION_uS;
-
- GSM_TDMA_FN_INC(sched->fn_counter_proc);
-
- /* Call frame callback */
- if (sched->clock_cb)
- sched->clock_cb(sched);
- }
-
- osmo_timer_schedule(&sched->clock_timer, 0,
- GSM_TDMA_FN_DURATION_uS - elapsed_us);
-}
-
-static void sched_clck_correct(struct trx_sched *sched,
- struct timespec *tv_now, uint32_t fn)
-{
- sched->fn_counter_proc = fn;
-
- /* Call frame callback */
- if (sched->clock_cb)
- sched->clock_cb(sched);
-
- /* Schedule first FN clock */
- sched->clock = *tv_now;
- memset(&sched->clock_timer, 0, sizeof(sched->clock_timer));
-
- sched->clock_timer.cb = sched_clck_tick;
- sched->clock_timer.data = sched;
- osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS);
-}
-
-int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
-{
- struct timespec tv_now, *tv_clock, elapsed;
- int64_t elapsed_us, elapsed_fn;
-
- /* Reset lost counter */
- sched->fn_counter_lost = 0;
-
- /* Get actual / previous frame time */
- osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
- tv_clock = &sched->clock;
-
- /* If this is the first CLCK IND */
- if (sched->state == SCH_CLCK_STATE_WAIT) {
- sched_clck_correct(sched, &tv_now, fn);
-
- LOGP(DSCH, LOGL_DEBUG, "Initial clock received: fn=%u\n", fn);
- sched->state = SCH_CLCK_STATE_OK;
-
- return 0;
- }
-
- LOGP(DSCH, LOGL_NOTICE, "Clock indication: fn=%u\n", fn);
-
- osmo_timer_del(&sched->clock_timer);
-
- /* Calculate elapsed time / frames since last processed fn */
- timespecsub(&tv_now, tv_clock, &elapsed);
- elapsed_us = (elapsed.tv_sec * 1000000) + (elapsed.tv_nsec / 1000);
- elapsed_fn = GSM_TDMA_FN_SUB(fn, sched->fn_counter_proc);
-
- if (elapsed_fn >= 135774)
- elapsed_fn -= GSM_TDMA_HYPERFRAME;
-
- /* Check for max clock skew */
- if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
- LOGP(DSCH, LOGL_NOTICE, "GSM clock skew: old fn=%u, "
- "new fn=%u\n", sched->fn_counter_proc, fn);
-
- sched_clck_correct(sched, &tv_now, fn);
- return 0;
- }
-
- LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %" PRId64 "\n",
- elapsed_fn * GSM_TDMA_FN_DURATION_uS - elapsed_us);
-
- /* Too many frames have been processed already */
- if (elapsed_fn < 0) {
- struct timespec duration;
- /**
- * Set clock to the time or last FN should
- * have been transmitted
- */
- duration.tv_nsec = (0 - elapsed_fn) * GSM_TDMA_FN_DURATION_nS;
- duration.tv_sec = duration.tv_nsec / 1000000000;
- duration.tv_nsec = duration.tv_nsec % 1000000000;
- timespecadd(&tv_now, &duration, tv_clock);
-
- /* Set time to the time our next FN has to be transmitted */
- osmo_timer_schedule(&sched->clock_timer, 0,
- GSM_TDMA_FN_DURATION_uS * (1 - elapsed_fn));
-
- return 0;
- }
-
- /* Transmit what we still need to transmit */
- while (fn != sched->fn_counter_proc) {
- GSM_TDMA_FN_INC(sched->fn_counter_proc);
-
- /* Call frame callback */
- if (sched->clock_cb)
- sched->clock_cb(sched);
- }
-
- /* Schedule next FN to be transmitted */
- *tv_clock = tv_now;
- osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS);
-
- return 0;
-}
-
-void sched_clck_reset(struct trx_sched *sched)
-{
- /* Reset internal state */
- sched->state = SCH_CLCK_STATE_WAIT;
-
- /* Stop clock timer */
- osmo_timer_del(&sched->clock_timer);
-
- /* Flush counters */
- sched->fn_counter_proc = 0;
- sched->fn_counter_lost = 0;
-}
diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c
deleted file mode 100644
index ae43ca94..00000000
--- a/src/host/trxcon/sched_lchan_common.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: common routines for lchan handlers
- *
- * (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * 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
- * (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.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <talloc.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-#include <osmocom/core/gsmtap_util.h>
-#include <osmocom/core/gsmtap.h>
-
-#include <osmocom/codec/codec.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trxcon.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */
-const uint8_t sched_nb_training_bits[8][26] = {
- {
- 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
- 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1,
- },
- {
- 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1,
- 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
- },
- {
- 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
- 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
- },
- {
- 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
- 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
- },
- {
- 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
- 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
- },
- {
- 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
- 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
- },
- {
- 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
- 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
- },
- {
- 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,
- 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
- },
-};
-
-/* Get a string representation of the burst buffer's completeness.
- * Examples: " ****.." (incomplete, 4/6 bursts)
- * " ****" (complete, all 4 bursts)
- * "**.***.." (incomplete, 5/8 bursts) */
-const char *burst_mask2str(const uint8_t *mask, int bits)
-{
- /* TODO: CSD is interleaved over 22 bursts, so the mask needs to be extended */
- static char buf[8 + 1];
- char *ptr = buf;
-
- OSMO_ASSERT(bits <= 8 && bits > 0);
-
- while (--bits >= 0)
- *(ptr++) = (*mask & (1 << bits)) ? '*' : '.';
- *ptr = '\0';
-
- return buf;
-}
-
-int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn,
- uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr,
- const uint8_t *data, size_t data_len)
-{
- const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[lchan_type];
-
- /* GSMTAP logging may not be enabled */
- if (gsmtap == NULL)
- return 0;
-
- /* Omit frames with unknown channel type */
- if (lchan_desc->gsmtap_chan_type == GSMTAP_CHANNEL_UNKNOWN)
- return 0;
-
- /* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */
- return gsmtap_send(gsmtap, band_arfcn, tn, lchan_desc->gsmtap_chan_type,
- lchan_desc->ss_nr, fn, signal_dbm, snr, data, data_len);
-}
-
-int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
- int bit_error_count, bool dec_failed, bool traffic)
-{
- const struct trx_meas_set *meas = &lchan->meas_avg;
- const struct trx_lchan_desc *lchan_desc;
- struct l1ctl_info_dl dl_hdr;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
-
- /* Fill in known downlink info */
- dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index;
- dl_hdr.link_id = lchan_desc->link_id;
- dl_hdr.band_arfcn = htons(trx->band_arfcn);
- dl_hdr.num_biterr = bit_error_count;
-
- /* sched_trx_meas_avg() gives us TDMA frame number of the first burst */
- dl_hdr.frame_nr = htonl(meas->fn);
-
- /* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */
- dl_hdr.rx_level = dbm2rxlev(meas->rssi);
-
- /* FIXME: set proper values */
- dl_hdr.snr = 0;
-
- /* Mark frame as broken if so */
- dl_hdr.fire_crc = dec_failed ? 2 : 0;
-
- /* Put a packet to higher layers */
- l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, traffic);
-
- /* Optional GSMTAP logging */
- if (l2_len > 0 && (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH)) {
- sched_gsmtap_send(lchan->type, meas->fn, ts->index,
- trx->band_arfcn, meas->rssi, 0, l2, l2_len);
- }
-
- return 0;
-}
-
-int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, bool traffic)
-{
- const struct trx_lchan_desc *lchan_desc;
- struct l1ctl_info_dl dl_hdr;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
-
- /* Zero-initialize DL header, because we don't set all fields */
- memset(&dl_hdr, 0x00, sizeof(struct l1ctl_info_dl));
-
- /* Fill in known downlink info */
- dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index;
- dl_hdr.link_id = lchan_desc->link_id;
- dl_hdr.band_arfcn = htons(trx->band_arfcn);
- dl_hdr.frame_nr = htonl(fn);
-
- l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, traffic);
-
- /* Optional GSMTAP logging */
- if (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH) {
- sched_gsmtap_send(lchan->type, fn, ts->index,
- trx->band_arfcn | ARFCN_UPLINK,
- 0, 0, lchan->prim->payload,
- lchan->prim->payload_len);
- }
-
- return 0;
-}
-
-/**
- * Composes a bad frame indication message
- * according to the current tch_mode.
- *
- * @param l2 Caller-allocated byte array
- * @param lchan Logical channel to generate BFI for
- * @return How much bytes were written
- */
-size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan)
-{
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- if (lchan->type == TRXC_TCHF) { /* Full Rate */
- memset(l2, 0x00, GSM_FR_BYTES);
- l2[0] = 0xd0;
- return GSM_FR_BYTES;
- } else { /* Half Rate */
- memset(l2 + 1, 0x00, GSM_HR_BYTES);
- l2[0] = 0x70; /* F = 0, FT = 111 */
- return GSM_HR_BYTES + 1;
- }
- case GSM48_CMODE_SPEECH_EFR: /* Enhanced Full Rate */
- memset(l2, 0x00, GSM_EFR_BYTES);
- l2[0] = 0xc0;
- return GSM_EFR_BYTES;
- case GSM48_CMODE_SPEECH_AMR: /* Adaptive Multi Rate */
- /* FIXME: AMR is not implemented yet */
- return 0;
- case GSM48_CMODE_SIGN:
- LOGP(DSCH, LOGL_ERROR, "BFI is not allowed in signalling mode\n");
- return 0;
- default:
- LOGP(DSCH, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
- return 0;
- }
-}
diff --git a/src/host/trxcon/sched_lchan_pdtch.c b/src/host/trxcon/sched_lchan_pdtch.c
deleted file mode 100644
index abbd480c..00000000
--- a/src/host/trxcon/sched_lchan_pdtch.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2018-2021 by Vadim Yanitskiy <axilirator@gmail.com>
- * Contributions 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 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.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/coding/gsm0503_coding.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas)
-{
- const struct trx_lchan_desc *lchan_desc;
- uint8_t l2[GPRS_L2_MAX_LEN], *mask;
- int n_errors, n_bits_total, rc;
- sbit_t *buffer, *offset;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Packet data received on %s: "
- "fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid);
-
- /* Align to the first burst of a block */
- if (*mask == 0x00 && bid != 0)
- return 0;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Store the measurements */
- sched_trx_meas_push(lchan, meas);
-
- /* Copy burst to buffer of 4 bursts */
- offset = buffer + bid * 116;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* Calculate AVG of the measurements */
- sched_trx_meas_avg(lchan, 4);
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
- "fn=%u (%u/%u) for %s\n",
- burst_mask2str(mask, 4), lchan->meas_avg.fn,
- lchan->meas_avg.fn % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
- /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
- }
-
- /* Keep the mask updated */
- *mask = *mask << 4;
-
- /* Attempt to decode */
- rc = gsm0503_pdtch_decode(l2, buffer,
- NULL, &n_errors, &n_bits_total);
- if (rc < 0) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad packet data frame "
- "at fn=%u (%u/%u) for %s\n", lchan->meas_avg.fn,
- lchan->meas_avg.fn % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
- }
-
- /* Determine L2 length */
- l2_len = rc > 0 ? rc : 0;
-
- /* Send a L2 frame to the higher layers */
- sched_send_dt_ind(trx, ts, lchan,
- l2, l2_len, n_errors, rc < 0, true);
-
- return 0;
-}
-
-
-int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- if (br->bid > 0) {
- /* If we have encoded bursts */
- if (*mask)
- goto send_burst;
- else
- return 0;
- }
-
- /* Encode payload */
- rc = gsm0503_pdtch_encode(buffer, lchan->prim->payload,
- lchan->prim->payload_len);
- if (rc < 0) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + br->bid * 116;
-
- /* Update mask */
- *mask |= (1 << br->bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(br->burst, 0, 3); /* TB */
- memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(br->burst + 61, tsc, 26); /* TSC */
- memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(br->burst + 145, 0, 3); /* TB */
- br->burst_len = GSM_BURST_LEN;
-
- LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, br->fn, ts->index, br->bid);
-
- /* If we have sent the last (4/4) burst */
- if ((*mask & 0x0f) == 0x0f) {
- /* Confirm data / traffic sending */
- sched_send_dt_conf(trx, ts, lchan, br->fn, true);
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c
deleted file mode 100644
index 25e1b440..00000000
--- a/src/host/trxcon/sched_lchan_rach.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2017-2021 by Vadim Yanitskiy <axilirator@gmail.com>
- * Contributions 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 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.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/coding/gsm0503_coding.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */
-#define RACH_EXT_TAIL_BITS_LEN 8
-#define RACH_SYNCH_SEQ_LEN 41
-#define RACH_PAYLOAD_LEN 36
-
-/* Extended tail bits (BN0..BN7) */
-static const ubit_t rach_ext_tail_bits[] = {
- 0, 0, 1, 1, 1, 0, 1, 0,
-};
-
-/* Synchronization (training) sequence types */
-enum rach_synch_seq_t {
- RACH_SYNCH_SEQ_UNKNOWN = -1,
- RACH_SYNCH_SEQ_TS0, /* GSM, GMSK (default) */
- RACH_SYNCH_SEQ_TS1, /* EGPRS, 8-PSK */
- RACH_SYNCH_SEQ_TS2, /* EGPRS, GMSK */
- RACH_SYNCH_SEQ_NUM
-};
-
-/* Synchronization (training) sequence bits */
-static const char rach_synch_seq_bits[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = {
- [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000",
- [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101",
- [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111",
-};
-
-/* Synchronization (training) sequence names */
-static struct value_string rach_synch_seq_names[] = {
- { RACH_SYNCH_SEQ_UNKNOWN, "UNKNOWN" },
- { RACH_SYNCH_SEQ_TS0, "TS0: GSM, GMSK" },
- { RACH_SYNCH_SEQ_TS1, "TS1: EGPRS, 8-PSK" },
- { RACH_SYNCH_SEQ_TS2, "TS2: EGPRS, GMSK" },
- { 0, NULL },
-};
-
-/* Obtain a to-be-transmitted RACH burst */
-int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br)
-{
- struct l1ctl_ext_rach_req *ext_req = NULL;
- struct l1ctl_rach_req *req = NULL;
- enum rach_synch_seq_t synch_seq;
- uint8_t *burst_ptr = br->burst;
- uint8_t payload[36];
- int i, rc;
-
- /* Is it extended (11-bit) RACH or not? */
- if (PRIM_IS_RACH11(lchan->prim)) {
- ext_req = (struct l1ctl_ext_rach_req *) lchan->prim->payload;
- synch_seq = ext_req->synch_seq;
-
- /* Check requested synch. sequence */
- if (synch_seq >= RACH_SYNCH_SEQ_NUM) {
- LOGP(DSCHD, LOGL_ERROR, "Unknown RACH synch. sequence=0x%02x\n", synch_seq);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -ENOTSUP;
- }
-
- /* Delay sending according to offset value */
- if (ext_req->offset-- > 0)
- return 0;
-
- /* Encode extended (11-bit) payload */
- rc = gsm0503_rach_ext_encode(payload, ext_req->ra11, trx->bsic, true);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Could not encode extended RACH burst "
- "(ra=%u bsic=%u)\n", ext_req->ra11, trx->bsic);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return rc;
- }
- } else if (PRIM_IS_RACH8(lchan->prim)) {
- req = (struct l1ctl_rach_req *) lchan->prim->payload;
- synch_seq = RACH_SYNCH_SEQ_TS0;
-
- /* Delay sending according to offset value */
- if (req->offset-- > 0)
- return 0;
-
- /* Encode regular (8-bit) payload */
- rc = gsm0503_rach_ext_encode(payload, req->ra, trx->bsic, false);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst "
- "(ra=%u bsic=%u)\n", req->ra, trx->bsic);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return rc;
- }
- } else {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %zu or %zu), "
- "so dropping...\n", lchan->prim->payload_len,
- sizeof(*req), sizeof(*ext_req));
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
-
- /* BN0-7: extended tail bits */
- memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
- burst_ptr += RACH_EXT_TAIL_BITS_LEN;
-
- /* BN8-48: chosen synch. (training) sequence */
- for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++)
- *(burst_ptr++) = rach_synch_seq_bits[synch_seq][i] == '1';
-
- /* BN49-84: encrypted bits (the payload) */
- memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
- burst_ptr += RACH_PAYLOAD_LEN;
-
- /* BN85-156: tail bits & extended guard period */
- memset(burst_ptr, 0, br->burst + GSM_BURST_LEN - burst_ptr);
- br->burst_len = GSM_BURST_LEN;
-
- LOGP(DSCHD, LOGL_NOTICE, "Scheduled %s RACH (%s) on fn=%u, tn=%u, lchan=%s\n",
- PRIM_IS_RACH11(lchan->prim) ? "extended (11-bit)" : "regular (8-bit)",
- get_value_string(rach_synch_seq_names, synch_seq), br->fn,
- ts->index, trx_lchan_desc[lchan->type].name);
-
- /* Confirm RACH request */
- l1ctl_tx_rach_conf(trx->l1l, trx->band_arfcn, br->fn);
-
- /* Optional GSMTAP logging */
- sched_gsmtap_send(lchan->type, br->fn, ts->index,
- trx->band_arfcn | ARFCN_UPLINK, 0, 0,
- PRIM_IS_RACH11(lchan->prim) ? (uint8_t *) &ext_req->ra11 : &req->ra,
- PRIM_IS_RACH11(lchan->prim) ? 2 : 1);
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c
deleted file mode 100644
index 1e38e963..00000000
--- a/src/host/trxcon/sched_lchan_tchf.c
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2017-2021 by Vadim Yanitskiy <axilirator@gmail.com>
- * Contributions 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 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.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/codec/codec.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas)
-{
- const struct trx_lchan_desc *lchan_desc;
- int n_errors = -1, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint8_t l2[128], *mask;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Align to the first burst of a block */
- if (*mask == 0x00 && bid != 0)
- return 0;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Store the measurements */
- sched_trx_meas_push(lchan, meas);
-
- /* Copy burst to end of buffer of 8 bursts */
- offset = buffer + bid * 116 + 464;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* Calculate AVG of the measurements */
- sched_trx_meas_avg(lchan, 8);
-
- /* Check for complete set of bursts */
- if ((*mask & 0xff) != 0xff) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) traffic frame at "
- "fn=%u (%u/%u) for %s\n",
- burst_mask2str(mask, 8), lchan->meas_avg.fn,
- lchan->meas_avg.fn % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
- /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
-
- }
-
- /* Keep the mask updated */
- *mask = *mask << 4;
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* FR */
- rc = gsm0503_tch_fr_decode(l2, buffer,
- 1, 0, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- rc = gsm0503_tch_fr_decode(l2, buffer,
- 1, 1, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n");
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
- return -EINVAL;
- }
-
- /* Shift buffer by 4 bursts for interleaving */
- memcpy(buffer, buffer + 464, 464);
-
- /* Check decoding result */
- if (rc < 4) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at "
- "fn=%u for %s\n", fn, lchan_desc->name);
-
- /* Send BFI */
- goto bfi;
- } else if (rc == GSM_MACBLOCK_LEN) {
- /* FACCH received, forward it to the higher layers */
- sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-
- /* Send BFI substituting a stolen TCH frame */
- n_errors = -1; /* ensure fake measurements */
- goto bfi;
- } else {
- /* A good TCH frame received */
- l2_len = rc;
- }
-
- /* Send a traffic frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, false, true);
-
-bfi:
- /* Didn't try to decode, fake measurements */
- if (n_errors < 0) {
- lchan->meas_avg = (struct trx_meas_set) {
- .fn = lchan->meas_avg.fn,
- .toa256 = 0,
- .rssi = -110,
- };
-
- /* No bursts => no errors */
- n_errors = 0;
- }
-
- /* BFI is not applicable in signalling mode */
- if (lchan->tch_mode == GSM48_CMODE_SIGN)
- return sched_send_dt_ind(trx, ts, lchan, NULL, 0,
- n_errors, true, false);
-
- /* Bad frame indication */
- l2_len = sched_bad_frame_ind(l2, lchan);
-
- /* Send a BFI frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, true, true);
-}
-
-int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- size_t l2_len;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- /* If we have encoded bursts */
- if (*mask)
- goto send_burst;
-
- /* Wait until a first burst in period */
- if (br->bid > 0)
- return 0;
-
- /* Check the current TCH mode */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* FR */
- l2_len = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- l2_len = GSM_EFR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, "
- "dropping frame...\n");
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, "
- "dropping frame...\n", lchan->tch_mode);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
- /* Determine and check the payload length */
- if (lchan->prim->payload_len == GSM_MACBLOCK_LEN) {
- l2_len = GSM_MACBLOCK_LEN; /* FACCH */
- } else if (lchan->prim->payload_len != l2_len) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu "
- "(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
-
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Shift buffer by 4 bursts back for interleaving */
- memcpy(buffer, buffer + 464, 464);
-
- /* Encode payload */
- rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + br->bid * 116;
-
- /* Update mask */
- *mask |= (1 << br->bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(br->burst, 0, 3); /* TB */
- memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(br->burst + 61, tsc, 26); /* TSC */
- memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(br->burst + 145, 0, 3); /* TB */
- br->burst_len = GSM_BURST_LEN;
-
- LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, br->fn, ts->index, br->bid);
-
- /* If we have sent the last (4/4) burst */
- if (*mask == 0x0f) {
- /* Confirm data / traffic sending */
- sched_send_dt_conf(trx, ts, lchan, br->fn, PRIM_IS_TCH(lchan->prim));
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_tchh.c b/src/host/trxcon/sched_lchan_tchh.c
deleted file mode 100644
index 6a5c4714..00000000
--- a/src/host/trxcon/sched_lchan_tchh.c
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2018-2021 by Vadim Yanitskiy <axilirator@gmail.com>
- * (C) 2018 by Harald Welte <laforge@gnumonks.org>
- * Contributions 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 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.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/codec/codec.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-static const uint8_t tch_h0_traffic_block_map[3][4] = {
- /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */
- { 0, 2, 4, 6 },
- { 4, 6, 8, 10 },
- { 8, 10, 0, 2 },
-};
-
-static const uint8_t tch_h1_traffic_block_map[3][4] = {
- /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */
- { 1, 3, 5, 7 },
- { 5, 7, 9, 11 },
- { 9, 11, 1, 3 },
-};
-
-static const uint8_t tch_h0_dl_facch_block_map[3][6] = {
- /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */
- { 4, 6, 8, 10, 13, 15 },
- { 13, 15, 17, 19, 21, 23 },
- { 21, 23, 0, 2, 4, 6 },
-};
-
-static const uint8_t tch_h0_ul_facch_block_map[3][6] = {
- /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */
- { 0, 2, 4, 6, 8, 10 },
- { 8, 10, 13, 15, 17, 19 },
- { 17, 19, 21, 23, 0, 2 },
-};
-
-static const uint8_t tch_h1_dl_facch_block_map[3][6] = {
- /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */
- { 5, 7, 9, 11, 14, 16 },
- { 14, 16, 18, 20, 22, 24 },
- { 22, 24, 1, 3, 5, 7 },
-};
-
-const uint8_t tch_h1_ul_facch_block_map[3][6] = {
- /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */
- { 1, 3, 5, 7, 9, 11 },
- { 9, 11, 14, 16, 18, 20 },
- { 18, 20, 22, 24, 1, 3 },
-};
-
-/**
- * Can a TCH/H block transmission be initiated / finished
- * on a given frame number and a given channel type?
- *
- * See GSM 05.02, clause 7, table 1
- *
- * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1)
- * @param fn the current frame number
- * @param ul Uplink or Downlink?
- * @param facch FACCH/H or traffic?
- * @param start init or end of transmission?
- * @return true (yes) or false (no)
- */
-bool sched_tchh_block_map_fn(enum trx_lchan_type chan,
- uint32_t fn, bool ul, bool facch, bool start)
-{
- uint8_t fn_mf;
- int i = 0;
-
- /* Just to be sure */
- OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1);
-
- /* Calculate a modulo */
- fn_mf = facch ? (fn % 26) : (fn % 13);
-
-#define MAP_GET_POS(map) \
- (start ? 0 : ARRAY_SIZE(map[i]) - 1)
-
-#define BLOCK_MAP_FN(map) \
- do { \
- if (map[i][MAP_GET_POS(map)] == fn_mf) \
- return true; \
- } while (++i < ARRAY_SIZE(map))
-
- /* Choose a proper block map */
- if (facch) {
- if (ul) {
- if (chan == TRXC_TCHH_0)
- BLOCK_MAP_FN(tch_h0_ul_facch_block_map);
- else
- BLOCK_MAP_FN(tch_h1_ul_facch_block_map);
- } else {
- if (chan == TRXC_TCHH_0)
- BLOCK_MAP_FN(tch_h0_dl_facch_block_map);
- else
- BLOCK_MAP_FN(tch_h1_dl_facch_block_map);
- }
- } else {
- if (chan == TRXC_TCHH_0)
- BLOCK_MAP_FN(tch_h0_traffic_block_map);
- else
- BLOCK_MAP_FN(tch_h1_traffic_block_map);
- }
-
- return false;
-}
-
-/**
- * Calculates a frame number of the first burst
- * using given frame number of the last burst.
- *
- * See GSM 05.02, clause 7, table 1
- *
- * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1)
- * @param last_fn frame number of the last burst
- * @param facch FACCH/H or traffic?
- * @return either frame number of the first burst,
- * or fn=last_fn if calculation failed
- */
-uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
- uint32_t last_fn, bool facch)
-{
- uint8_t fn_mf, fn_diff;
- int i = 0;
-
- /* Just to be sure */
- OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1);
-
- /* Calculate a modulo */
- fn_mf = facch ? (last_fn % 26) : (last_fn % 13);
-
-#define BLOCK_FIRST_FN(map) \
- do { \
- if (map[i][ARRAY_SIZE(map[i]) - 1] == fn_mf) { \
- fn_diff = GSM_TDMA_FN_DIFF(fn_mf, map[i][0]); \
- return GSM_TDMA_FN_SUB(last_fn, fn_diff); \
- } \
- } while (++i < ARRAY_SIZE(map))
-
- /* Choose a proper block map */
- if (facch) {
- if (chan == TRXC_TCHH_0)
- BLOCK_FIRST_FN(tch_h0_dl_facch_block_map);
- else
- BLOCK_FIRST_FN(tch_h1_dl_facch_block_map);
- } else {
- if (chan == TRXC_TCHH_0)
- BLOCK_FIRST_FN(tch_h0_traffic_block_map);
- else
- BLOCK_FIRST_FN(tch_h1_traffic_block_map);
- }
-
- LOGP(DSCHD, LOGL_ERROR, "Failed to calculate TDMA "
- "frame number of the first burst of %s block, "
- "using the current fn=%u\n", facch ?
- "FACCH/H" : "TCH/H", last_fn);
-
- /* Couldn't calculate the first fn, return the last */
- return last_fn;
-}
-
-int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas)
-{
- const struct trx_lchan_desc *lchan_desc;
- int n_errors = -1, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint8_t l2[128], *mask;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- if (*mask == 0x00) {
- /* Align to the first burst */
- if (bid > 0)
- return 0;
-
- /* Align reception of the first FACCH/H frame */
- if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- if (!sched_tchh_facch_start(lchan->type, fn, 0))
- return 0;
- } else { /* or TCH/H traffic frame */
- if (!sched_tchh_traffic_start(lchan->type, fn, 0))
- return 0;
- }
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Store the measurements */
- sched_trx_meas_push(lchan, meas);
-
- /* Copy burst to the end of buffer of 6 bursts */
- offset = buffer + bid * 116 + 464;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until the second burst */
- if (bid != 1)
- return 0;
-
- /* Wait for complete set of bursts */
- if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- /* FACCH/H is interleaved over 6 bursts */
- if ((*mask & 0x3f) != 0x3f)
- goto bfi_shift;
- } else {
- /* Traffic is interleaved over 4 bursts */
- if ((*mask & 0x0f) != 0x0f)
- goto bfi_shift;
- }
-
- /* Skip decoding attempt in case of FACCH/H */
- if (lchan->dl_ongoing_facch) {
- lchan->dl_ongoing_facch = false;
- goto bfi_shift; /* 2/2 BFI */
- }
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* HR */
- rc = gsm0503_tch_hr_decode(l2, buffer,
- !sched_tchh_facch_end(lchan->type, fn, 0),
- &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n");
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
- return -EINVAL;
- }
-
- /* Shift buffer by 4 bursts for interleaving */
- memcpy(buffer, buffer + 232, 232);
- memcpy(buffer + 232, buffer + 464, 232);
-
- /* Shift burst mask */
- *mask = *mask << 2;
-
- /* Check decoding result */
- if (rc < 4) {
- /* Calculate AVG of the measurements (assuming 4 bursts) */
- sched_trx_meas_avg(lchan, 4);
-
- LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame (%s) "
- "at fn=%u on %s (rc=%d)\n", burst_mask2str(mask, 6),
- lchan->meas_avg.fn, lchan_desc->name, rc);
-
- /* Send BFI */
- goto bfi;
- } else if (rc == GSM_MACBLOCK_LEN) {
- /* Skip decoding of the next 2 stolen bursts */
- lchan->dl_ongoing_facch = true;
-
- /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */
- sched_trx_meas_avg(lchan, 6);
-
- /* FACCH/H received, forward to the higher layers */
- sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-
- /* Send BFI substituting 1/2 stolen TCH frames */
- n_errors = -1; /* ensure fake measurements */
- goto bfi;
- } else {
- /* A good TCH frame received */
- l2_len = rc;
-
- /* Calculate AVG of the measurements (traffic takes 4 bursts) */
- sched_trx_meas_avg(lchan, 4);
- }
-
- /* Send a traffic frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, false, true);
-
-bfi_shift:
- /* Shift buffer */
- memcpy(buffer, buffer + 232, 232);
- memcpy(buffer + 232, buffer + 464, 232);
-
- /* Shift burst mask */
- *mask = *mask << 2;
-
-bfi:
- /* Didn't try to decode, fake measurements */
- if (n_errors < 0) {
- lchan->meas_avg = (struct trx_meas_set) {
- .fn = sched_tchh_block_dl_first_fn(lchan->type, fn, false),
- .toa256 = 0,
- .rssi = -110,
- };
-
- /* No bursts => no errors */
- n_errors = 0;
- }
-
- /* BFI is not applicable in signalling mode */
- if (lchan->tch_mode == GSM48_CMODE_SIGN)
- return sched_send_dt_ind(trx, ts, lchan, NULL, 0,
- n_errors, true, false);
-
- /* Bad frame indication */
- l2_len = sched_bad_frame_ind(l2, lchan);
-
- /* Send a BFI frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, true, true);
-}
-
-int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- size_t l2_len;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- if (br->bid > 0) {
- /* Align to the first burst */
- if (*mask == 0x00)
- return 0;
- goto send_burst;
- }
-
- if (*mask == 0x00) {
- /* Align transmission of the first FACCH/H frame */
- if (lchan->tch_mode == GSM48_CMODE_SIGN)
- if (!sched_tchh_facch_start(lchan->type, br->fn, 1))
- return 0;
- }
-
- /* Shift buffer by 2 bursts back for interleaving */
- memcpy(buffer, buffer + 232, 232);
-
- /* Also shift TX burst mask */
- *mask = *mask << 2;
-
- /* If FACCH/H blocks are still pending */
- if (lchan->ul_facch_blocks > 2) {
- memcpy(buffer + 232, buffer + 464, 232);
- goto send_burst;
- }
-
- /* Check the current TCH mode */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* HR */
- l2_len = GSM_HR_BYTES + 1;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, "
- "dropping frame...\n");
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, "
- "dropping frame...\n", lchan->tch_mode);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Determine payload length */
- if (PRIM_IS_FACCH(lchan->prim)) {
- l2_len = GSM_MACBLOCK_LEN; /* FACCH */
- } else if (lchan->prim->payload_len != l2_len) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu "
- "(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Encode the payload */
- rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* A FACCH/H frame occupies 6 bursts */
- if (PRIM_IS_FACCH(lchan->prim))
- lchan->ul_facch_blocks = 6;
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + br->bid * 116;
-
- /* Update mask */
- *mask |= (1 << br->bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(br->burst, 0, 3); /* TB */
- memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(br->burst + 61, tsc, 26); /* TSC */
- memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(br->burst + 145, 0, 3); /* TB */
- br->burst_len = GSM_BURST_LEN;
-
- LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, br->fn, ts->index, br->bid);
-
- /* In case of a FACCH/H frame, one block less */
- if (lchan->ul_facch_blocks)
- lchan->ul_facch_blocks--;
-
- if ((*mask & 0x0f) == 0x0f) {
- /**
- * If no more FACCH/H blocks pending,
- * confirm data / traffic sending
- */
- if (!lchan->ul_facch_blocks)
- sched_send_dt_conf(trx, ts, lchan, br->fn,
- PRIM_IS_TCH(lchan->prim));
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c
deleted file mode 100644
index 41677ec1..00000000
--- a/src/host/trxcon/sched_lchan_xcch.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2017-2021 by Vadim Yanitskiy <axilirator@gmail.com>
- * Contributions 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 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.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/coding/gsm0503_coding.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas)
-{
- const struct trx_lchan_desc *lchan_desc;
- uint8_t l2[GSM_MACBLOCK_LEN], *mask;
- int n_errors, n_bits_total, rc;
- sbit_t *buffer, *offset;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Align to the first burst of a block */
- if (*mask == 0x00 && bid != 0)
- return 0;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Store the measurements */
- sched_trx_meas_push(lchan, meas);
-
- /* Copy burst to buffer of 4 bursts */
- offset = buffer + bid * 116;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* Calculate AVG of the measurements */
- sched_trx_meas_avg(lchan, 4);
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
- "fn=%u (%u/%u) for %s\n",
- burst_mask2str(mask, 4), lchan->meas_avg.fn,
- lchan->meas_avg.fn % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
- /* NOTE: xCCH has an insane amount of redundancy for error
- * correction, so even just 2 valid bursts might be enough
- * to reconstruct some L2 frames. This is why we do not
- * abort here. */
- }
-
- /* Keep the mask updated */
- *mask = *mask << 4;
-
- /* Attempt to decode */
- rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad data frame at fn=%u "
- "(%u/%u) for %s\n", lchan->meas_avg.fn,
- lchan->meas_avg.fn % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
-
- /**
- * We should anyway send dummy frame for
- * proper measurement reporting...
- */
- return sched_send_dt_ind(trx, ts, lchan, NULL, 0,
- n_errors, true, false);
- }
-
- /* Send a L2 frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-}
-
-int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- if (br->bid > 0) {
- /* If we have encoded bursts */
- if (*mask)
- goto send_burst;
- else
- return 0;
- }
-
- /* Check the prim payload length */
- if (lchan->prim->payload_len != GSM_MACBLOCK_LEN) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %u), "
- "so dropping...\n", lchan->prim->payload_len, GSM_MACBLOCK_LEN);
-
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Encode payload */
- rc = gsm0503_xcch_encode(buffer, lchan->prim->payload);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + br->bid * 116;
-
- /* Update mask */
- *mask |= (1 << br->bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(br->burst, 0, 3); /* TB */
- memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(br->burst + 61, tsc, 26); /* TSC */
- memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(br->burst + 145, 0, 3); /* TB */
- br->burst_len = GSM_BURST_LEN;
-
- LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, br->fn, ts->index, br->bid);
-
- /* If we have sent the last (4/4) burst */
- if ((*mask & 0x0f) == 0x0f) {
- /* Confirm data sending */
- sched_send_dt_conf(trx, ts, lchan, br->fn, false);
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_mframe.c b/src/host/trxcon/sched_mframe.c
deleted file mode 100644
index 9b759af3..00000000
--- a/src/host/trxcon/sched_mframe.c
+++ /dev/null
@@ -1,2101 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: channel combinations, burst mapping
- *
- * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
- * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
- * (C) 2015 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 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 "sched_trx.h"
-
-/* Non-combined CCCH */
-static const struct trx_frame frame_bcch[51] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_BCCH, 0, TRXC_RACH, 0 },
- { TRXC_BCCH, 1, TRXC_RACH, 0 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_IDLE, 0, TRXC_RACH, 0 },
-};
-
-/* Combined CCCH+SDCCH4 */
-static const struct trx_frame frame_bcch_sdcch4[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_SACCH4_2, 0 },
- { TRXC_CCCH, 1, TRXC_SACCH4_2, 1 },
- { TRXC_CCCH, 2, TRXC_SACCH4_2, 2 },
- { TRXC_CCCH, 3, TRXC_SACCH4_2, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_3, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_3, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_3, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_SACCH4_0, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_SACCH4_0, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_SACCH4_0, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_SACCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 1, TRXC_SDCCH4_2, 0 },
- { TRXC_SACCH4_1, 2, TRXC_SDCCH4_2, 1 },
- { TRXC_SACCH4_1, 3, TRXC_SDCCH4_2, 2 },
- { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 },
-
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_SACCH4_0, 0 },
- { TRXC_CCCH, 1, TRXC_SACCH4_0, 1 },
- { TRXC_CCCH, 2, TRXC_SACCH4_0, 2 },
- { TRXC_CCCH, 3, TRXC_SACCH4_0, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_1, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_1, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_1, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_1, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_SACCH4_2, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_SACCH4_2, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_SACCH4_2, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_SACCH4_2, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 1, TRXC_SDCCH4_2, 0 },
- { TRXC_SACCH4_3, 2, TRXC_SDCCH4_2, 1 },
- { TRXC_SACCH4_3, 3, TRXC_SDCCH4_2, 2 },
- { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 },
-};
-
-static const struct trx_frame frame_bcch_sdcch4_cbch[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_IDLE, 0 },
- { TRXC_CCCH, 1, TRXC_IDLE, 1 },
- { TRXC_CCCH, 2, TRXC_IDLE, 2 },
- { TRXC_CCCH, 3, TRXC_IDLE, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_3, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_3, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_3, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_SACCH4_0, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_SACCH4_0, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_SACCH4_0, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_SACCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 1, TRXC_IDLE, 0 },
- { TRXC_SACCH4_1, 2, TRXC_IDLE, 1 },
- { TRXC_SACCH4_1, 3, TRXC_IDLE, 2 },
- { TRXC_IDLE, 0, TRXC_IDLE, 3 },
-
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_SACCH4_0, 0 },
- { TRXC_CCCH, 1, TRXC_SACCH4_0, 1 },
- { TRXC_CCCH, 2, TRXC_SACCH4_0, 2 },
- { TRXC_CCCH, 3, TRXC_SACCH4_0, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_1, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_1, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_1, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_1, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_IDLE, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_IDLE, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_IDLE, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_IDLE, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 1, TRXC_IDLE, 0 },
- { TRXC_SACCH4_3, 2, TRXC_IDLE, 1 },
- { TRXC_SACCH4_3, 3, TRXC_IDLE, 2 },
- { TRXC_IDLE, 0, TRXC_IDLE, 3 },
-};
-
-static const struct trx_frame frame_sdcch8[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_5, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_5, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_5, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_5, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_SACCH8_6, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_SACCH8_6, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_SACCH8_6, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_SACCH8_6, 3 },
- { TRXC_SDCCH8_2, 0, TRXC_SACCH8_7, 0 },
- { TRXC_SDCCH8_2, 1, TRXC_SACCH8_7, 1 },
- { TRXC_SDCCH8_2, 2, TRXC_SACCH8_7, 2 },
- { TRXC_SDCCH8_2, 3, TRXC_SACCH8_7, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_0, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_0, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_0, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_0, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_1, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_1, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_1, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_1, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_SACCH8_2, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_SACCH8_2, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_SACCH8_2, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_SACCH8_2, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_3, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_3, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_3, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_3, 3, TRXC_SACCH8_0, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 },
-
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_1, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_SACCH8_2, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_SACCH8_2, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_SACCH8_2, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_SACCH8_2, 3 },
- { TRXC_SDCCH8_2, 0, TRXC_SACCH8_3, 0 },
- { TRXC_SDCCH8_2, 1, TRXC_SACCH8_3, 1 },
- { TRXC_SDCCH8_2, 2, TRXC_SACCH8_3, 2 },
- { TRXC_SDCCH8_2, 3, TRXC_SACCH8_3, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_4, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_4, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_4, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_4, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_5, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_5, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_5, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_5, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_SACCH8_6, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_SACCH8_6, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_SACCH8_6, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_SACCH8_6, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_7, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_7, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_7, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_7, 3, TRXC_SACCH8_4, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 },
-};
-
-static const struct trx_frame frame_sdcch8_cbch[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_5, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_5, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_5, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_5, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_SACCH8_6, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_SACCH8_6, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_SACCH8_6, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_SACCH8_6, 3 },
- { TRXC_SDCCH8_CBCH, 0, TRXC_SACCH8_7, 0 },
- { TRXC_SDCCH8_CBCH, 1, TRXC_SACCH8_7, 1 },
- { TRXC_SDCCH8_CBCH, 2, TRXC_SACCH8_7, 2 },
- { TRXC_SDCCH8_CBCH, 3, TRXC_SACCH8_7, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_IDLE, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_IDLE, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_IDLE, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_0, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_0, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_0, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_0, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_1, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_1, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_1, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_1, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_IDLE, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_IDLE, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_IDLE, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_IDLE, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_3, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_3, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_3, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_3, 3, TRXC_SACCH8_0, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 },
-
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_1, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_IDLE, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_IDLE, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_IDLE, 3 },
- { TRXC_SDCCH8_CBCH, 0, TRXC_SACCH8_3, 0 },
- { TRXC_SDCCH8_CBCH, 1, TRXC_SACCH8_3, 1 },
- { TRXC_SDCCH8_CBCH, 2, TRXC_SACCH8_3, 2 },
- { TRXC_SDCCH8_CBCH, 3, TRXC_SACCH8_3, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_IDLE, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_IDLE, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_IDLE, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_4, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_4, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_4, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_4, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_5, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_5, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_5, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_5, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_SACCH8_6, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_SACCH8_6, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_SACCH8_6, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_SACCH8_6, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_7, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_7, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_7, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_7, 3, TRXC_SACCH8_4, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 },
-};
-
-static const struct trx_frame frame_tchf_ts0[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts1[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
-};
-
-static const struct trx_frame frame_tchf_ts2[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts3[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
-};
-
-static const struct trx_frame frame_tchf_ts4[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts5[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
-};
-
-static const struct trx_frame frame_tchf_ts6[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts7[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
-};
-
-static const struct trx_frame frame_tchh_ts01[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
-};
-
-static const struct trx_frame frame_tchh_ts23[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
-};
-
-static const struct trx_frame frame_tchh_ts45[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
-};
-
-static const struct trx_frame frame_tchh_ts67[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
-};
-
-static const struct trx_frame frame_pdch[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 0, TRXC_PTCCH, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 1, TRXC_PTCCH, 1 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 2, TRXC_PTCCH, 2 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 3, TRXC_PTCCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-/* Logical channel mask for a single channel */
-#define M64(x) \
- ((uint64_t) 0x01 << x)
-
-/* Logical channel mask for BCCH+CCCH */
-#define M64_BCCH_CCCH \
- (uint64_t) 0x00 \
- | M64(TRXC_FCCH) \
- | M64(TRXC_SCH) \
- | M64(TRXC_BCCH) \
- | M64(TRXC_RACH) \
- | M64(TRXC_CCCH)
-
-/* Logical channel mask for SDCCH4 (with SACCH, all sub-channels) */
-#define M64_SDCCH4 \
- (uint64_t) 0x00 \
- | M64(TRXC_SDCCH4_0) | M64(TRXC_SACCH4_0) \
- | M64(TRXC_SDCCH4_1) | M64(TRXC_SACCH4_1) \
- | M64(TRXC_SDCCH4_2) | M64(TRXC_SACCH4_2) \
- | M64(TRXC_SDCCH4_3) | M64(TRXC_SACCH4_3)
-
-/* Logical channel mask for SDCCH8 (with SACCH, all sub-channels) */
-#define M64_SDCCH8 \
- (uint64_t) 0x00 \
- | M64(TRXC_SDCCH8_0) | M64(TRXC_SACCH8_0) \
- | M64(TRXC_SDCCH8_1) | M64(TRXC_SACCH8_1) \
- | M64(TRXC_SDCCH8_2) | M64(TRXC_SACCH8_2) \
- | M64(TRXC_SDCCH8_3) | M64(TRXC_SACCH8_3) \
- | M64(TRXC_SDCCH8_4) | M64(TRXC_SACCH8_4) \
- | M64(TRXC_SDCCH8_5) | M64(TRXC_SACCH8_5) \
- | M64(TRXC_SDCCH8_6) | M64(TRXC_SACCH8_6) \
- | M64(TRXC_SDCCH8_7) | M64(TRXC_SACCH8_7)
-
-/* Logical channel mask for TCH/F (with SACCH) */
-#define M64_TCHF \
- (uint64_t) 0x00 \
- | M64(TRXC_TCHF) | M64(TRXC_SACCHTF)
-
-/* Logical channel mask for TCH/H (with SACCH, all sub-channels) */
-#define M64_TCHH \
- (uint64_t) 0x00 \
- | M64(TRXC_TCHH_0) | M64(TRXC_SACCHTH_0) \
- | M64(TRXC_TCHH_1) | M64(TRXC_SACCHTH_1)
-
-/**
- * A few notes about frame count:
- *
- * 26 frame multiframe - traffic multiframe
- * 51 frame multiframe - control multiframe
- *
- * 102 = 2 x 51 frame multiframe
- * 104 = 4 x 26 frame multiframe
- */
-static const struct trx_multiframe layouts[] = {
- {
- GSM_PCHAN_NONE, "NONE",
- 0, 0xff,
- 0x00,
- NULL
- },
- {
- GSM_PCHAN_CCCH, "BCCH+CCCH",
- 51, 0xff,
- M64_BCCH_CCCH,
- frame_bcch
- },
- {
- GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4",
- 102, 0xff,
- M64_BCCH_CCCH | M64_SDCCH4,
- frame_bcch_sdcch4
- },
- {
- GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH",
- 102, 0xff,
- M64_BCCH_CCCH | M64_SDCCH4 | M64(TRXC_SDCCH4_CBCH),
- frame_bcch_sdcch4_cbch
- },
- {
- GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8",
- 102, 0xff,
- M64_SDCCH8,
- frame_sdcch8
- },
- {
- GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH",
- 102, 0xff,
- M64_SDCCH8 | M64(TRXC_SDCCH8_CBCH),
- frame_sdcch8_cbch
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x01,
- M64_TCHF,
- frame_tchf_ts0
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x02,
- M64_TCHF,
- frame_tchf_ts1
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x04,
- M64_TCHF,
- frame_tchf_ts2
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x08,
- M64_TCHF,
- frame_tchf_ts3
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x10,
- M64_TCHF,
- frame_tchf_ts4
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x20,
- M64_TCHF,
- frame_tchf_ts5
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x40,
- M64_TCHF,
- frame_tchf_ts6
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x80,
- M64_TCHF,
- frame_tchf_ts7
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x03,
- M64_TCHH,
- frame_tchh_ts01
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x0c,
- M64_TCHH,
- frame_tchh_ts23
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x30,
- M64_TCHH,
- frame_tchh_ts45
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0xc0,
- M64_TCHH,
- frame_tchh_ts67
- },
- {
- GSM_PCHAN_PDCH, "PDCH",
- 104, 0xff,
- M64(TRXC_PDTCH) | M64(TRXC_PTCCH),
- frame_pdch
- },
-};
-
-const struct trx_multiframe *sched_mframe_layout(
- enum gsm_phys_chan_config config, int tn)
-{
- int i, ts_allowed;
-
- for (i = 0; i < ARRAY_SIZE(layouts); i++) {
- ts_allowed = layouts[i].slotmask & (0x01 << tn);
- if (layouts[i].chan_config == config && ts_allowed)
- return &layouts[i];
- }
-
- return NULL;
-}
diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c
deleted file mode 100644
index 94733203..00000000
--- a/src/host/trxcon/sched_prim.c
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: primitive management
- *
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * 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
- * (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.
- *
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <talloc.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/linuxlist.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "trx_if.h"
-#include "logging.h"
-
-/**
- * Initializes a new primitive by allocating memory
- * and filling some meta-information (e.g. lchan type).
- *
- * @param ctx parent talloc context
- * @param prim external prim pointer (will point to the allocated prim)
- * @param pl_len prim payload length
- * @param chan_nr RSL channel description (used to set a proper chan)
- * @param link_id RSL link description (used to set a proper chan)
- * @return zero in case of success, otherwise a error number
- */
-int sched_prim_init(void *ctx, struct trx_ts_prim **prim,
- size_t pl_len, uint8_t chan_nr, uint8_t link_id)
-{
- enum trx_lchan_type lchan_type;
- struct trx_ts_prim *new_prim;
- uint8_t len;
-
- /* Determine lchan type */
- lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id);
- if (!lchan_type) {
- LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type "
- "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id);
- return -EINVAL;
- }
-
- /* How much memory do we need? */
- len = sizeof(struct trx_ts_prim); /* Primitive header */
- len += pl_len; /* Requested payload size */
-
- /* Allocate a new primitive */
- new_prim = talloc_zero_size(ctx, len);
- if (new_prim == NULL) {
- LOGP(DSCH, LOGL_ERROR, "Failed to allocate memory\n");
- return -ENOMEM;
- }
-
- /* Init primitive header */
- new_prim->payload_len = pl_len;
- new_prim->chan = lchan_type;
-
- /* Set external pointer */
- *prim = new_prim;
-
- return 0;
-}
-
-/**
- * Adds a primitive to the end of transmit queue of a particular
- * timeslot, whose index is parsed from chan_nr.
- *
- * @param trx TRX instance
- * @param prim to be enqueued primitive
- * @param chan_nr RSL channel description
- * @return zero in case of success, otherwise a error number
- */
-int sched_prim_push(struct trx_instance *trx,
- struct trx_ts_prim *prim, uint8_t chan_nr)
-{
- struct trx_ts *ts;
- uint8_t tn;
-
- /* Determine TS index */
- tn = chan_nr & 0x7;
-
- /* Check whether required timeslot is allocated and configured */
- ts = trx->ts_list[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn);
- return -EINVAL;
- }
-
- /**
- * Change talloc context of primitive
- * from trx to the parent ts
- */
- talloc_steal(ts, prim);
-
- /* Add primitive to TS transmit queue */
- llist_add_tail(&prim->list, &ts->tx_prims);
-
- return 0;
-}
-
-/**
- * Composes a new primitive using either cached (if populated),
- * or "dummy" Measurement Report message.
- *
- * @param lchan lchan to assign a primitive
- * @return SACCH primitive to be transmitted
- */
-static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan)
-{
- struct trx_ts_prim *prim;
- uint8_t *mr_src_ptr;
- bool cached;
- int rc;
-
- /* "Dummy" Measurement Report */
- static const uint8_t meas_rep_dummy[] = {
- /* L1 SACCH pseudo-header */
- 0x0f, 0x00,
-
- /* LAPDm header */
- 0x01, 0x03, 0x49,
-
- /* RR Management messages, Measurement Report */
- 0x06, 0x15,
-
- /* Measurement results (see 3GPP TS 44.018, section 10.5.2.20):
- * 0... .... = BA-USED: 0
- * .0.. .... = DTX-USED: DTX was not used
- * ..11 0110 = RXLEV-FULL-SERVING-CELL: -57 <= x < -56 dBm (54)
- * 0... .... = 3G-BA-USED: 0
- * .1.. .... = MEAS-VALID: The measurement results are not valid
- * ..11 0110 = RXLEV-SUB-SERVING-CELL: -57 <= x < -56 dBm (54)
- * 0... .... = SI23_BA_USED: 0
- * .000 .... = RXQUAL-FULL-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0)
- * .... 000. = RXQUAL-SUB-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0)
- * .... ...1 11.. .... = NO-NCELL-M: Neighbour cell information not available */
- 0x36, 0x76, 0x01, 0xc0,
-
- /* 0** -- Padding with zeroes */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- };
-
- /* Allocate a new primitive */
- rc = sched_prim_init(lchan, &prim, GSM_MACBLOCK_LEN,
- trx_lchan_desc[lchan->type].chan_nr, TRX_CH_LID_SACCH);
- OSMO_ASSERT(rc == 0);
-
- /* Check if the MR cache is populated (verify LAPDm header) */
- cached = (lchan->sacch.mr_cache[2] != 0x00
- && lchan->sacch.mr_cache[3] != 0x00
- && lchan->sacch.mr_cache[4] != 0x00);
- if (cached) { /* Use the cached one */
- mr_src_ptr = lchan->sacch.mr_cache;
- lchan->sacch.mr_cache_usage++;
- } else { /* Use "dummy" one */
- mr_src_ptr = (uint8_t *) meas_rep_dummy;
- }
-
- /* Compose a new Measurement Report primitive */
- memcpy(prim->payload, mr_src_ptr, GSM_MACBLOCK_LEN);
-
- /**
- * Update the L1 SACCH pseudo-header (only for cached MRs)
- *
- * TODO: filling of the actual values into cached Measurement
- * Reports would break the distance spoofing feature. If it
- * were known whether the spoofing is enabled or not, we could
- * decide whether to update the cached L1 SACCH header here.
- */
- if (!cached) {
- prim->payload[0] = lchan->ts->trx->tx_power;
- prim->payload[1] = lchan->ts->trx->ta;
- }
-
- /* Inform about the cache usage count */
- if (cached && lchan->sacch.mr_cache_usage > 5) {
- LOGP(DSCHD, LOGL_NOTICE, "SACCH MR cache usage count=%u > 5 "
- "on lchan=%s => ancient measurements, please fix!\n",
- lchan->sacch.mr_cache_usage,
- trx_lchan_desc[lchan->type].name);
- }
-
- LOGP(DSCHD, LOGL_NOTICE, "Using a %s Measurement Report "
- "on lchan=%s\n", (cached ? "cached" : "dummy"),
- trx_lchan_desc[lchan->type].name);
-
- return prim;
-}
-
-/**
- * Dequeues a SACCH primitive from transmit queue, if present.
- * Otherwise dequeues a cached Measurement Report (the last
- * received one). Finally, if the cache is empty, a "dummy"
- * measurement report is used.
- *
- * According to 3GPP TS 04.08, section 3.4.1, SACCH channel
- * accompanies either a traffic or a signaling channel. It
- * has the particularity that continuous transmission must
- * occur in both directions, so on the Uplink direction
- * measurement result messages are sent at each possible
- * occasion when nothing else has to be sent. The LAPDm
- * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not
- * applicable on SACCH channels!
- *
- * Unfortunately, 3GPP TS 04.08 doesn't clearly state
- * which "else messages" besides Measurement Reports
- * can be send by the MS on SACCH channels. However,
- * in sub-clause 3.4.1 it's stated that the interval
- * between two successive measurement result messages
- * shall not exceed one L2 frame.
- *
- * @param queue transmit queue to take a prim from
- * @param lchan lchan to assign a primitive
- * @return SACCH primitive to be transmitted
- */
-static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue,
- struct trx_lchan_state *lchan)
-{
- struct trx_ts_prim *prim_nmr = NULL;
- struct trx_ts_prim *prim_mr = NULL;
- struct trx_ts_prim *prim;
- bool mr_now;
-
- /* Shall we transmit MR now? */
- mr_now = !lchan->sacch.mr_tx_last;
-
-#define PRIM_IS_MR(prim) \
- (prim->payload[5] == GSM48_PDISC_RR \
- && prim->payload[6] == GSM48_MT_RR_MEAS_REP)
-
- /* Iterate over all primitives in the queue */
- llist_for_each_entry(prim, queue, list) {
- /* We are looking for particular channel */
- if (prim->chan != lchan->type)
- continue;
-
- /* Look for a Measurement Report */
- if (!prim_mr && PRIM_IS_MR(prim))
- prim_mr = prim;
-
- /* Look for anything else */
- if (!prim_nmr && !PRIM_IS_MR(prim))
- prim_nmr = prim;
-
- /* Should we look further? */
- if (mr_now && prim_mr)
- break; /* MR was found */
- else if (!mr_now && prim_nmr)
- break; /* something else was found */
- }
-
- LOGP(DSCHD, LOGL_DEBUG, "SACCH MR selection on lchan=%s: "
- "mr_tx_last=%d prim_mr=%p prim_nmr=%p\n",
- trx_lchan_desc[lchan->type].name,
- lchan->sacch.mr_tx_last,
- prim_mr, prim_nmr);
-
- /* Prioritize non-MR prim if possible */
- if (mr_now && prim_mr)
- prim = prim_mr;
- else if (!mr_now && prim_nmr)
- prim = prim_nmr;
- else if (!mr_now && prim_mr)
- prim = prim_mr;
- else /* Nothing was found */
- prim = NULL;
-
- /* Have we found what we were looking for? */
- if (prim) /* Dequeue if so */
- llist_del(&prim->list);
- else /* Otherwise compose a new MR */
- prim = prim_compose_mr(lchan);
-
- /* Update the cached report */
- if (prim == prim_mr) {
- memcpy(lchan->sacch.mr_cache,
- prim->payload, GSM_MACBLOCK_LEN);
- lchan->sacch.mr_cache_usage = 0;
-
- LOGP(DSCHD, LOGL_DEBUG, "SACCH MR cache has been updated "
- "for lchan=%s\n", trx_lchan_desc[lchan->type].name);
- }
-
- /* Update the MR transmission state */
- lchan->sacch.mr_tx_last = PRIM_IS_MR(prim);
-
- LOGP(DSCHD, LOGL_DEBUG, "SACCH decision on lchan=%s: %s\n",
- trx_lchan_desc[lchan->type].name, PRIM_IS_MR(prim) ?
- "Measurement Report" : "data frame");
-
- return prim;
-}
-
-/* Dequeues a primitive of a given channel type */
-static struct trx_ts_prim *prim_dequeue_one(struct llist_head *queue,
- enum trx_lchan_type lchan_type)
-{
- struct trx_ts_prim *prim;
-
- /**
- * There is no need to use the 'safe' list iteration here
- * as an item removal is immediately followed by return.
- */
- llist_for_each_entry(prim, queue, list) {
- if (prim->chan == lchan_type) {
- llist_del(&prim->list);
- return prim;
- }
- }
-
- return NULL;
-}
-
-/**
- * Dequeues either a FACCH, or a speech TCH primitive
- * of a given channel type (Lm or Bm).
- *
- * Note: we could avoid 'lchan_type' parameter and just
- * check the prim's channel type using CHAN_IS_TCH(),
- * but the current approach is a bit more flexible,
- * and allows one to have both sub-slots of TCH/H
- * enabled on same timeslot e.g. for testing...
- *
- * @param queue transmit queue to take a prim from
- * @param lchan_type required channel type of a primitive,
- * e.g. TRXC_TCHF, TRXC_TCHH_0, or TRXC_TCHH_1
- * @param facch FACCH (true) or speech (false) prim?
- * @return either a FACCH, or a TCH primitive if found,
- * otherwise NULL
- */
-static struct trx_ts_prim *prim_dequeue_tch(struct llist_head *queue,
- enum trx_lchan_type lchan_type, bool facch)
-{
- struct trx_ts_prim *prim;
-
- /**
- * There is no need to use the 'safe' list iteration here
- * as an item removal is immediately followed by return.
- */
- llist_for_each_entry(prim, queue, list) {
- if (prim->chan != lchan_type)
- continue;
-
- /* Either FACCH, or not FACCH */
- if (PRIM_IS_FACCH(prim) != facch)
- continue;
-
- llist_del(&prim->list);
- return prim;
- }
-
- return NULL;
-}
-
-/**
- * Dequeues either a TCH/F, or a FACCH/F prim (preferred).
- * If a FACCH/F prim is found, one TCH/F prim is being
- * dropped (i.e. replaced).
- *
- * @param queue a transmit queue to take a prim from
- * @return either a FACCH/F, or a TCH/F primitive,
- * otherwise NULL
- */
-static struct trx_ts_prim *prim_dequeue_tch_f(struct llist_head *queue)
-{
- struct trx_ts_prim *facch;
- struct trx_ts_prim *tch;
-
- /* Attempt to find a pair of both FACCH/F and TCH/F frames */
- facch = prim_dequeue_tch(queue, TRXC_TCHF, true);
- tch = prim_dequeue_tch(queue, TRXC_TCHF, false);
-
- /* Prioritize FACCH/F, if found */
- if (facch) {
- /* One TCH/F prim is replaced */
- if (tch)
- talloc_free(tch);
- return facch;
- } else if (tch) {
- /* Only TCH/F prim was found */
- return tch;
- } else {
- /* Nothing was found, e.g. when only SACCH frames are in queue */
- return NULL;
- }
-}
-
-/**
- * Dequeues either a TCH/H, or a FACCH/H prim (preferred).
- * If a FACCH/H prim is found, two TCH/H prims are being
- * dropped (i.e. replaced).
- *
- * According to GSM 05.02, the following blocks can be used
- * to carry FACCH/H data (see clause 7, table 1 of 9):
- *
- * UL FACCH/H0:
- * B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2)
- *
- * UL FACCH/H1:
- * B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3)
- *
- * where the numbers within brackets are fn % 26.
- *
- * @param queue transmit queue to take a prim from
- * @param fn the current frame number
- * @param lchan_type required channel type of a primitive,
- * @return either a FACCH/H, or a TCH/H primitive,
- * otherwise NULL
- */
-static struct trx_ts_prim *prim_dequeue_tch_h(struct llist_head *queue,
- uint32_t fn, enum trx_lchan_type lchan_type)
-{
- struct trx_ts_prim *facch;
- struct trx_ts_prim *tch;
- bool facch_now;
-
- /* May we initiate an UL FACCH/H frame transmission now? */
- facch_now = sched_tchh_facch_start(lchan_type, fn, true);
- if (!facch_now) /* Just dequeue a TCH/H prim */
- goto no_facch;
-
- /* If there are no FACCH/H prims in the queue */
- facch = prim_dequeue_tch(queue, lchan_type, true);
- if (!facch) /* Just dequeue a TCH/H prim */
- goto no_facch;
-
- /* FACCH/H prim replaces two TCH/F prims */
- tch = prim_dequeue_tch(queue, lchan_type, false);
- if (tch) {
- /* At least one TCH/H prim is dropped */
- talloc_free(tch);
-
- /* Attempt to find another */
- tch = prim_dequeue_tch(queue, lchan_type, false);
- if (tch) /* Drop the second TCH/H prim */
- talloc_free(tch);
- }
-
- return facch;
-
-no_facch:
- return prim_dequeue_tch(queue, lchan_type, false);
-}
-
-/**
- * Dequeues a single primitive of required type
- * from a specified transmit queue.
- *
- * @param queue a transmit queue to take a prim from
- * @param fn the current frame number (used for FACCH/H)
- * @param lchan logical channel state
- * @return a primitive or NULL if not found
- */
-struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue,
- uint32_t fn, struct trx_lchan_state *lchan)
-{
- /* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */
- if (CHAN_IS_SACCH(lchan->type))
- return prim_dequeue_sacch(queue, lchan);
-
- /* There is nothing to dequeue */
- if (llist_empty(queue))
- return NULL;
-
- switch (lchan->type) {
- /* TCH/F requires FACCH/F prioritization */
- case TRXC_TCHF:
- return prim_dequeue_tch_f(queue);
-
- /* FACCH/H prioritization is a bit more complex */
- case TRXC_TCHH_0:
- case TRXC_TCHH_1:
- return prim_dequeue_tch_h(queue, fn, lchan->type);
-
- /* Other kinds of logical channels */
- default:
- return prim_dequeue_one(queue, lchan->type);
- }
-}
-
-/**
- * Drops the current primitive of specified logical channel
- *
- * @param lchan a logical channel to drop prim from
- */
-void sched_prim_drop(struct trx_lchan_state *lchan)
-{
- /* Forget this primitive */
- talloc_free(lchan->prim);
- lchan->prim = NULL;
-}
-
-/**
- * Assigns a dummy primitive to a lchan depending on its type.
- * Could be used when there is nothing to transmit, but
- * CBTX (Continuous Burst Transmission) is assumed.
- *
- * @param lchan lchan to assign a primitive
- * @return zero in case of success, otherwise a error code
- */
-int sched_prim_dummy(struct trx_lchan_state *lchan)
-{
- enum trx_lchan_type chan = lchan->type;
- uint8_t tch_mode = lchan->tch_mode;
- struct trx_ts_prim *prim;
- uint8_t prim_buffer[40];
- size_t prim_len = 0;
- int i;
-
- /**
- * TS 144.006, section 8.4.2.3 "Fill frames"
- * A fill frame is a UI command frame for SAPI 0, P=0
- * and with an information field of 0 octet length.
- */
- static const uint8_t lapdm_fill_frame[] = {
- 0x01, 0x03, 0x01, 0x2b,
- /* Pending part is to be randomized */
- };
-
- /* Make sure that there is no existing primitive */
- OSMO_ASSERT(lchan->prim == NULL);
- /* Not applicable for SACCH! */
- OSMO_ASSERT(!CHAN_IS_SACCH(lchan->type));
-
- /**
- * Determine what actually should be generated:
- * TCH in GSM48_CMODE_SIGN: LAPDm fill frame;
- * TCH in other modes: silence frame;
- * other channels: LAPDm fill frame.
- */
- if (CHAN_IS_TCH(chan) && TCH_MODE_IS_SPEECH(tch_mode)) {
- /* Bad frame indication */
- prim_len = sched_bad_frame_ind(prim_buffer, lchan);
- } else if (CHAN_IS_TCH(chan) && TCH_MODE_IS_DATA(tch_mode)) {
- /* FIXME: should we do anything for CSD? */
- return 0;
- } else {
- /* Copy LAPDm fill frame's header */
- memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame));
-
- /**
- * TS 144.006, section 5.2 "Frame delimitation and fill bits"
- * Except for the first octet containing fill bits which shall
- * be set to the binary value "00101011", each fill bit should
- * be set to a random value when sent by the network.
- */
- for (i = sizeof(lapdm_fill_frame); i < GSM_MACBLOCK_LEN; i++)
- prim_buffer[i] = (uint8_t) rand();
-
- /* Define a prim length */
- prim_len = GSM_MACBLOCK_LEN;
- }
-
- /* Nothing to allocate / assign */
- if (!prim_len)
- return 0;
-
- /* Allocate a new primitive */
- prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len);
- if (prim == NULL)
- return -ENOMEM;
-
- /* Init primitive header */
- prim->payload_len = prim_len;
- prim->chan = lchan->type;
-
- /* Fill in the payload */
- memcpy(prim->payload, prim_buffer, prim_len);
-
- /* Assign the current prim */
- lchan->prim = prim;
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting a dummy / silence frame "
- "on lchan=%s\n", trx_lchan_desc[chan].name);
-
- return 0;
-}
-
-/**
- * Flushes a queue of primitives
- *
- * @param list list of prims going to be flushed
- */
-void sched_prim_flush_queue(struct llist_head *list)
-{
- struct trx_ts_prim *prim, *prim_next;
-
- llist_for_each_entry_safe(prim, prim_next, list, list) {
- llist_del(&prim->list);
- talloc_free(prim);
- }
-}
diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c
deleted file mode 100644
index adcf198b..00000000
--- a/src/host/trxcon/sched_trx.c
+++ /dev/null
@@ -1,839 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: GSM PHY routines
- *
- * (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * 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
- * (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.
- *
- */
-
-#include <error.h>
-#include <errno.h>
-#include <string.h>
-#include <talloc.h>
-#include <stdbool.h>
-
-#include <osmocom/gsm/a5.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include <osmocom/core/bits.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/linuxlist.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "trx_if.h"
-#include "logging.h"
-
-static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
-
-static void sched_frame_clck_cb(struct trx_sched *sched)
-{
- struct trx_instance *trx = (struct trx_instance *) sched->data;
- struct sched_burst_req br[TRX_TS_COUNT];
- const struct trx_frame *frame;
- struct trx_lchan_state *lchan;
- trx_lchan_tx_func *handler;
- enum trx_lchan_type chan;
- uint8_t offset;
- struct trx_ts *ts;
- int i;
-
- /* Advance TDMA frame number in order to give the transceiver
- * more time to handle the burst before the actual transmission. */
- const uint32_t fn = GSM_TDMA_FN_SUM(sched->fn_counter_proc,
- sched->fn_counter_advance);
-
- /* Iterate over timeslot list */
- for (i = 0; i < TRX_TS_COUNT; i++) {
- /* Initialize the buffer for this timeslot */
- br[i] = (struct sched_burst_req) {
- .fn = fn,
- .tn = i,
- .pwr = trx->tx_power,
- .burst_len = 0, /* NOPE.ind */
- };
-
- /* Timeslot is not allocated */
- ts = trx->ts_list[i];
- if (ts == NULL)
- continue;
-
- /* Timeslot is not configured */
- if (ts->mf_layout == NULL)
- continue;
-
- /* Get frame from multiframe */
- offset = fn % ts->mf_layout->period;
- frame = ts->mf_layout->frames + offset;
-
- /* Get required info from frame */
- br[i].bid = frame->ul_bid;
- chan = frame->ul_chan;
- handler = trx_lchan_desc[chan].tx_fn;
-
- /* Omit lchans without handler */
- if (!handler)
- continue;
-
- /* Make sure that lchan was allocated and activated */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- continue;
-
- /* Omit inactive lchans */
- if (!lchan->active)
- continue;
-
- /**
- * If we aren't processing any primitive yet,
- * attempt to obtain a new one from queue
- */
- if (lchan->prim == NULL)
- lchan->prim = sched_prim_dequeue(&ts->tx_prims, fn, lchan);
-
- /* TODO: report TX buffers health to the higher layers */
-
- /* If CBTX (Continuous Burst Transmission) is assumed */
- if (trx_lchan_desc[chan].flags & TRX_CH_FLAG_CBTX) {
- /**
- * Probably, a TX buffer is empty. Nevertheless,
- * we shall continuously transmit anything on
- * CBTX channels.
- */
- if (lchan->prim == NULL)
- sched_prim_dummy(lchan);
- }
-
- /* If there is no primitive, do nothing */
- if (lchan->prim == NULL)
- continue;
-
- /* Handover RACH needs to be handled regardless of the
- * current channel type and the associated handler. */
- if (PRIM_IS_RACH(lchan->prim) && lchan->prim->chan != TRXC_RACH)
- handler = trx_lchan_desc[TRXC_RACH].tx_fn;
-
- /* Poke lchan handler */
- handler(trx, ts, lchan, &br[i]);
-
- /* Perform A5/X burst encryption if required */
- if (lchan->a5.algo)
- sched_trx_a5_burst_enc(lchan, &br[i]);
- }
-
- /* Send all bursts for this TDMA frame */
- for (i = 0; i < ARRAY_SIZE(br); i++)
- trx_if_tx_burst(trx, &br[i]);
-}
-
-int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance)
-{
- struct trx_sched *sched;
-
- if (!trx)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "Init scheduler\n");
-
- /* Obtain a scheduler instance from TRX */
- sched = &trx->sched;
-
- /* Register frame clock callback */
- sched->clock_cb = sched_frame_clck_cb;
-
- /* Set pointers */
- sched = &trx->sched;
- sched->data = trx;
-
- /* Set frame counter advance */
- sched->fn_counter_advance = fn_advance;
-
- return 0;
-}
-
-int sched_trx_shutdown(struct trx_instance *trx)
-{
- int i;
-
- if (!trx)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "Shutdown scheduler\n");
-
- /* Free all potentially allocated timeslots */
- for (i = 0; i < TRX_TS_COUNT; i++)
- sched_trx_del_ts(trx, i);
-
- return 0;
-}
-
-int sched_trx_reset(struct trx_instance *trx, bool reset_clock)
-{
- int i;
-
- if (!trx)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "Reset scheduler %s\n",
- reset_clock ? "and clock counter" : "");
-
- /* Free all potentially allocated timeslots */
- for (i = 0; i < TRX_TS_COUNT; i++)
- sched_trx_del_ts(trx, i);
-
- /* Stop and reset clock counter if required */
- if (reset_clock)
- sched_clck_reset(&trx->sched);
-
- return 0;
-}
-
-struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn)
-{
- /* Make sure that ts isn't allocated yet */
- if (trx->ts_list[tn] != NULL) {
- LOGP(DSCH, LOGL_ERROR, "Timeslot #%u already allocated\n", tn);
- return NULL;
- }
-
- LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn);
-
- /* Allocate a new one */
- trx->ts_list[tn] = talloc_zero(trx, struct trx_ts);
-
- /* Add backpointer */
- trx->ts_list[tn]->trx = trx;
-
- /* Assign TS index */
- trx->ts_list[tn]->index = tn;
-
- return trx->ts_list[tn];
-}
-
-void sched_trx_del_ts(struct trx_instance *trx, int tn)
-{
- struct trx_lchan_state *lchan, *lchan_next;
- struct trx_ts *ts;
-
- /* Find ts in list */
- ts = trx->ts_list[tn];
- if (ts == NULL)
- return;
-
- LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn);
-
- /* Deactivate all logical channels */
- sched_trx_deactivate_all_lchans(ts);
-
- /* Free channel states */
- llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
- llist_del(&lchan->list);
- talloc_free(lchan);
- }
-
- /* Flush queue primitives for TX */
- sched_prim_flush_queue(&ts->tx_prims);
-
- /* Remove ts from list and free memory */
- trx->ts_list[tn] = NULL;
- talloc_free(ts);
-
- /* Notify transceiver about that */
- trx_if_cmd_setslot(trx, tn, 0);
-}
-
-#define LAYOUT_HAS_LCHAN(layout, lchan) \
- (layout->lchan_mask & ((uint64_t) 0x01 << lchan))
-
-int sched_trx_configure_ts(struct trx_instance *trx, int tn,
- enum gsm_phys_chan_config config)
-{
- struct trx_lchan_state *lchan;
- enum trx_lchan_type type;
- struct trx_ts *ts;
-
- /* Try to find specified ts */
- ts = trx->ts_list[tn];
- if (ts != NULL) {
- /* Reconfiguration of existing one */
- sched_trx_reset_ts(trx, tn);
- } else {
- /* Allocate a new one if doesn't exist */
- ts = sched_trx_add_ts(trx, tn);
- if (ts == NULL)
- return -ENOMEM;
- }
-
- /* Choose proper multiframe layout */
- ts->mf_layout = sched_mframe_layout(config, tn);
- if (!ts->mf_layout)
- return -EINVAL;
- if (ts->mf_layout->chan_config != config)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "(Re)configure TDMA timeslot #%u as %s\n",
- tn, ts->mf_layout->name);
-
- /* Init queue primitives for TX */
- INIT_LLIST_HEAD(&ts->tx_prims);
- /* Init logical channels list */
- INIT_LLIST_HEAD(&ts->lchans);
-
- /* Allocate channel states */
- for (type = 0; type < _TRX_CHAN_MAX; type++) {
- if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type))
- continue;
-
- /* Allocate a channel state */
- lchan = talloc_zero(ts, struct trx_lchan_state);
- if (!lchan)
- return -ENOMEM;
-
- /* set backpointer */
- lchan->ts = ts;
-
- /* Set channel type */
- lchan->type = type;
-
- /* Add to the list of channel states */
- llist_add_tail(&lchan->list, &ts->lchans);
-
- /* Enable channel automatically if required */
- if (trx_lchan_desc[type].flags & TRX_CH_FLAG_AUTO)
- sched_trx_activate_lchan(ts, type);
- }
-
- /* Notify transceiver about TS activation */
- /* FIXME: set proper channel type */
- trx_if_cmd_setslot(trx, tn, 1);
-
- return 0;
-}
-
-int sched_trx_reset_ts(struct trx_instance *trx, int tn)
-{
- struct trx_lchan_state *lchan, *lchan_next;
- struct trx_ts *ts;
-
- /* Try to find specified ts */
- ts = trx->ts_list[tn];
- if (ts == NULL)
- return -EINVAL;
-
- /* Undefine multiframe layout */
- ts->mf_layout = NULL;
-
- /* Flush queue primitives for TX */
- sched_prim_flush_queue(&ts->tx_prims);
-
- /* Deactivate all logical channels */
- sched_trx_deactivate_all_lchans(ts);
-
- /* Free channel states */
- llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
- llist_del(&lchan->list);
- talloc_free(lchan);
- }
-
- /* Notify transceiver about that */
- trx_if_cmd_setslot(trx, tn, 0);
-
- return 0;
-}
-
-int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo,
- uint8_t *key, uint8_t key_len)
-{
- struct trx_lchan_state *lchan;
-
- /* Prevent NULL-pointer deference */
- if (!ts)
- return -EINVAL;
-
- /* Make sure we can store this key */
- if (key_len > MAX_A5_KEY_LEN)
- return -ERANGE;
-
- /* Iterate over all allocated logical channels */
- llist_for_each_entry(lchan, &ts->lchans, list) {
- /* Omit inactive channels */
- if (!lchan->active)
- continue;
-
- /* Set key length and algorithm */
- lchan->a5.key_len = key_len;
- lchan->a5.algo = algo;
-
- /* Copy requested key */
- if (key_len)
- memcpy(lchan->a5.key, key, key_len);
- }
-
- return 0;
-}
-
-struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts,
- enum trx_lchan_type chan)
-{
- struct trx_lchan_state *lchan;
-
- llist_for_each_entry(lchan, &ts->lchans, list)
- if (lchan->type == chan)
- return lchan;
-
- return NULL;
-}
-
-int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode)
-{
- const struct trx_lchan_desc *lchan_desc;
- struct trx_lchan_state *lchan;
- int rc = 0;
-
- /* Prevent NULL-pointer deference */
- if (ts == NULL) {
- LOGP(DSCH, LOGL_ERROR, "Timeslot isn't configured\n");
- return -EINVAL;
- }
-
- /* Iterate over all allocated lchans */
- llist_for_each_entry(lchan, &ts->lchans, list) {
- lchan_desc = &trx_lchan_desc[lchan->type];
-
- if (lchan_desc->chan_nr == (chan_nr & 0xf8)) {
- if (active) {
- rc |= sched_trx_activate_lchan(ts, lchan->type);
- lchan->tch_mode = tch_mode;
- } else
- rc |= sched_trx_deactivate_lchan(ts, lchan->type);
- }
- }
-
- return rc;
-}
-
-int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan)
-{
- const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[chan];
- struct trx_lchan_state *lchan;
-
- /* Try to find requested logical channel */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- return -EINVAL;
-
- if (lchan->active) {
- LOGP(DSCH, LOGL_ERROR, "Logical channel %s already activated "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
- return -EINVAL;
- }
-
- LOGP(DSCH, LOGL_NOTICE, "Activating lchan=%s "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
-
- /* Conditionally allocate memory for bursts */
- if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) {
- lchan->rx_bursts = talloc_zero_size(lchan,
- lchan_desc->burst_buf_size);
- if (lchan->rx_bursts == NULL)
- return -ENOMEM;
- }
-
- if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) {
- lchan->tx_bursts = talloc_zero_size(lchan,
- lchan_desc->burst_buf_size);
- if (lchan->tx_bursts == NULL)
- return -ENOMEM;
- }
-
- /* Finally, update channel status */
- lchan->active = 1;
-
- return 0;
-}
-
-static void sched_trx_reset_lchan(struct trx_lchan_state *lchan)
-{
- /* Prevent NULL-pointer deference */
- OSMO_ASSERT(lchan != NULL);
-
- /* Print some TDMA statistics for Downlink */
- if (trx_lchan_desc[lchan->type].rx_fn && lchan->active) {
- LOGP(DSCH, LOGL_DEBUG, "TDMA statistics for lchan=%s on ts=%u: "
- "%lu DL frames have been processed, "
- "%lu lost (compensated), last fn=%u\n",
- trx_lchan_desc[lchan->type].name, lchan->ts->index,
- lchan->tdma.num_proc, lchan->tdma.num_lost,
- lchan->tdma.last_proc);
- }
-
- /* Reset internal state variables */
- lchan->rx_burst_mask = 0x00;
- lchan->tx_burst_mask = 0x00;
-
- /* Free burst memory */
- talloc_free(lchan->rx_bursts);
- talloc_free(lchan->tx_bursts);
-
- lchan->rx_bursts = NULL;
- lchan->tx_bursts = NULL;
-
- /* Forget the current prim */
- sched_prim_drop(lchan);
-
- /* Channel specific stuff */
- if (CHAN_IS_TCH(lchan->type)) {
- lchan->dl_ongoing_facch = 0;
- lchan->ul_facch_blocks = 0;
-
- lchan->tch_mode = GSM48_CMODE_SIGN;
-
- /* Reset AMR state */
- memset(&lchan->amr, 0x00, sizeof(lchan->amr));
- } else if (CHAN_IS_SACCH(lchan->type)) {
- /* Reset SACCH state */
- memset(&lchan->sacch, 0x00, sizeof(lchan->sacch));
- }
-
- /* Reset ciphering state */
- memset(&lchan->a5, 0x00, sizeof(lchan->a5));
-
- /* Reset TDMA frame statistics */
- memset(&lchan->tdma, 0x00, sizeof(lchan->tdma));
-}
-
-int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan)
-{
- struct trx_lchan_state *lchan;
-
- /* Try to find requested logical channel */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- return -EINVAL;
-
- if (!lchan->active) {
- LOGP(DSCH, LOGL_ERROR, "Logical channel %s already deactivated "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
- return -EINVAL;
- }
-
- LOGP(DSCH, LOGL_DEBUG, "Deactivating lchan=%s "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
-
- /* Reset internal state, free memory */
- sched_trx_reset_lchan(lchan);
-
- /* Update activation flag */
- lchan->active = 0;
-
- return 0;
-}
-
-void sched_trx_deactivate_all_lchans(struct trx_ts *ts)
-{
- struct trx_lchan_state *lchan;
-
- LOGP(DSCH, LOGL_DEBUG, "Deactivating all logical channels "
- "on ts=%d\n", ts->index);
-
- llist_for_each_entry(lchan, &ts->lchans, list) {
- /* Omit inactive channels */
- if (!lchan->active)
- continue;
-
- /* Reset internal state, free memory */
- sched_trx_reset_lchan(lchan);
-
- /* Update activation flag */
- lchan->active = 0;
- }
-}
-
-enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr)
-{
- uint8_t cbits = chan_nr >> 3;
-
- if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
- return GSM_PCHAN_TCH_F;
- else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0))
- return GSM_PCHAN_TCH_H;
- else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0))
- return GSM_PCHAN_CCCH_SDCCH4;
- else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0))
- return GSM_PCHAN_SDCCH8_SACCH8C;
- else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4)
- return GSM_PCHAN_CCCH_SDCCH4_CBCH;
- else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8)
- return GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
- else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH)
- return GSM_PCHAN_PDCH;
-
- return GSM_PCHAN_NONE;
-}
-
-enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr,
- uint8_t link_id)
-{
- int i;
-
- /* Iterate over all known lchan types */
- for (i = 0; i < _TRX_CHAN_MAX; i++)
- if (trx_lchan_desc[i].chan_nr == (chan_nr & 0xf8))
- if (trx_lchan_desc[i].link_id == link_id)
- return i;
-
- return TRXC_IDLE;
-}
-
-static void sched_trx_a5_burst_dec(struct trx_lchan_state *lchan,
- uint32_t fn, sbit_t *burst)
-{
- ubit_t ks[114];
- int i;
-
- /* Generate keystream for a DL burst */
- osmo_a5(lchan->a5.algo, lchan->a5.key, fn, ks, NULL);
-
- /* Apply keystream over ciphertext */
- for (i = 0; i < 57; i++) {
- if (ks[i])
- burst[i + 3] *= -1;
- if (ks[i + 57])
- burst[i + 88] *= -1;
- }
-}
-
-static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan,
- struct sched_burst_req *br)
-{
- ubit_t ks[114];
- int i;
-
- /* Generate keystream for an UL burst */
- osmo_a5(lchan->a5.algo, lchan->a5.key, br->fn, NULL, ks);
-
- /* Apply keystream over plaintext */
- for (i = 0; i < 57; i++) {
- br->burst[i + 3] ^= ks[i];
- br->burst[i + 88] ^= ks[i + 57];
- }
-}
-
-static int subst_frame_loss(struct trx_lchan_state *lchan,
- trx_lchan_rx_func *handler,
- uint32_t fn)
-{
- const struct trx_multiframe *mf;
- const struct trx_frame *fp;
- int elapsed, i;
-
- /* Wait until at least one TDMA frame is processed */
- if (lchan->tdma.num_proc == 0)
- return -EAGAIN;
-
- /* Short alias for the current multiframe */
- mf = lchan->ts->mf_layout;
-
- /* Calculate how many frames elapsed since the last received one.
- * The algorithm is based on GSM::FNDelta() from osmo-trx. */
- elapsed = fn - lchan->tdma.last_proc;
- if (elapsed >= GSM_TDMA_HYPERFRAME / 2)
- elapsed -= GSM_TDMA_HYPERFRAME;
- else if (elapsed < -GSM_TDMA_HYPERFRAME / 2)
- elapsed += GSM_TDMA_HYPERFRAME;
-
- /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */
- if (elapsed < 0) {
- /* This burst has already been substituted by a dummy burst (all bits set to zero),
- * so better drop it. Otherwise we risk to get undefined behavior in handler(). */
- LOGP(DSCHD, LOGL_ERROR, "(%s) Rx burst with fn=%u older than the last "
- "processed fn=%u (see OS#4658) => dropping\n",
- trx_lchan_desc[lchan->type].name,
- fn, lchan->tdma.last_proc);
- return -EALREADY;
- }
-
- /* Check how many frames we (potentially) need to compensate */
- if (elapsed > mf->period) {
- LOGP(DSCHD, LOGL_NOTICE, "Too many (>%u) contiguous TDMA frames elapsed (%d) "
- "since the last processed fn=%u (current %u)\n",
- mf->period, elapsed, lchan->tdma.last_proc, fn);
- return -EIO;
- } else if (elapsed == 0) {
- LOGP(DSCHD, LOGL_ERROR, "No TDMA frames elapsed since the last processed "
- "fn=%u, must be a bug?\n", lchan->tdma.last_proc);
- return -EIO;
- }
-
- static const sbit_t bits[148] = { 0 };
- struct trx_meas_set fake_meas = {
- .fn = lchan->tdma.last_proc,
- .rssi = -120,
- .toa256 = 0,
- };
-
- /* Traverse from fp till the current frame */
- for (i = 0; i < elapsed - 1; i++) {
- fp = &mf->frames[GSM_TDMA_FN_INC(fake_meas.fn) % mf->period];
- if (fp->dl_chan != lchan->type)
- continue;
-
- LOGP(DSCHD, LOGL_NOTICE, "Substituting lost TDMA frame %u on %s\n",
- fake_meas.fn, trx_lchan_desc[lchan->type].name);
-
- handler(lchan->ts->trx, lchan->ts, lchan,
- fake_meas.fn, fp->dl_bid,
- bits, &fake_meas);
-
- /* Update TDMA frame statistics */
- lchan->tdma.last_proc = fake_meas.fn;
- lchan->tdma.num_proc++;
- lchan->tdma.num_lost++;
- }
-
- return 0;
-}
-
-int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t fn, sbit_t *bits, uint16_t nbits,
- const struct trx_meas_set *meas)
-{
- struct trx_lchan_state *lchan;
- const struct trx_frame *frame;
- struct trx_ts *ts;
-
- trx_lchan_rx_func *handler;
- enum trx_lchan_type chan;
- uint8_t offset, bid;
- int rc;
-
- /* Check whether required timeslot is allocated and configured */
- ts = trx->ts_list[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DSCHD, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, "
- "ignoring burst...\n", tn);
- return -EINVAL;
- }
-
- /* Get frame from multiframe */
- offset = fn % ts->mf_layout->period;
- frame = ts->mf_layout->frames + offset;
-
- /* Get required info from frame */
- bid = frame->dl_bid;
- chan = frame->dl_chan;
- handler = trx_lchan_desc[chan].rx_fn;
-
- /* Omit bursts which have no handler, like IDLE bursts.
- * TODO: handle noise indications during IDLE frames. */
- if (!handler)
- return -ENODEV;
-
- /* Find required channel state */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- return -ENODEV;
-
- /* Ensure that channel is active */
- if (!lchan->active)
- return 0;
-
- /* Compensate lost TDMA frames (if any) */
- rc = subst_frame_loss(lchan, handler, fn);
- if (rc == -EALREADY)
- return rc;
-
- /* Perform A5/X decryption if required */
- if (lchan->a5.algo)
- sched_trx_a5_burst_dec(lchan, fn, bits);
-
- /* Put burst to handler */
- handler(trx, ts, lchan, fn, bid, bits, meas);
-
- /* Update TDMA frame statistics */
- lchan->tdma.last_proc = fn;
-
- if (++lchan->tdma.num_proc == 0) {
- /* Theoretically, we may have an integer overflow of num_proc counter.
- * As a consequence, subst_frame_loss() will be unable to compensate
- * one (potentionally lost) Downlink burst. On practice, it would
- * happen once in 4615 * 10e-6 * (2 ^ 32 - 1) seconds or ~6 years. */
- LOGP(DSCHD, LOGL_NOTICE, "Too many TDMA frames have been processed. "
- "Are you running trxcon for more than 6 years?!?\n");
- lchan->tdma.num_proc = 1;
- }
-
- return 0;
-}
-
-#define MEAS_HIST_FIRST(hist) \
- (&hist->buf[0])
-#define MEAS_HIST_LAST(hist) \
- (MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1)
-
-/* Add a new set of measurements to the history */
-void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas)
-{
- struct trx_lchan_meas_hist *hist = &lchan->meas_hist;
-
- /* Find a new position where to store the measurements */
- if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL)
- hist->head = MEAS_HIST_FIRST(hist);
- else
- hist->head++;
-
- *hist->head = *meas;
-}
-
-/* Calculate the AVG of n measurements from the history */
-void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n)
-{
- struct trx_lchan_meas_hist *hist = &lchan->meas_hist;
- struct trx_meas_set *meas = hist->head;
- int toa256_sum = 0;
- int rssi_sum = 0;
- int i;
-
- OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf));
- OSMO_ASSERT(meas != NULL);
-
- /* Traverse backwards up to n entries, calculate the sum */
- for (i = 0; i < n; i++) {
- toa256_sum += meas->toa256;
- rssi_sum += meas->rssi;
-
- /* Do not go below the first burst */
- if (i + 1 == n)
- break;
-
- if (meas == MEAS_HIST_FIRST(hist))
- meas = MEAS_HIST_LAST(hist);
- else
- meas--;
- }
-
- /* Calculate the AVG */
- lchan->meas_avg.toa256 = toa256_sum / n;
- lchan->meas_avg.rssi = rssi_sum / n;
-
- /* As a bonus, store TDMA frame number of the first burst */
- lchan->meas_avg.fn = meas->fn;
-}
diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h
deleted file mode 100644
index 74b41e35..00000000
--- a/src/host/trxcon/sched_trx.h
+++ /dev/null
@@ -1,415 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/bits.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/linuxlist.h>
-
-#include "logging.h"
-#include "scheduler.h"
-
-#define GSM_BURST_LEN 148
-#define GSM_BURST_PL_LEN 116
-
-#define GPRS_BURST_LEN GSM_BURST_LEN
-#define EDGE_BURST_LEN 444
-
-#define GPRS_L2_MAX_LEN 54
-#define EDGE_L2_MAX_LEN 155
-
-#define TRX_CH_LID_DEDIC 0x00
-#define TRX_CH_LID_SACCH 0x40
-
-/* Osmocom-specific extension for PTCCH (see 3GPP TS 45.002, section 3.3.4.2).
- * Shall be used to distinguish PTCCH and PDTCH channels on a PDCH time-slot. */
-#define TRX_CH_LID_PTCCH 0x80
-
-/* Is a channel related to PDCH (GPRS) */
-#define TRX_CH_FLAG_PDCH (1 << 0)
-/* Should a channel be activated automatically */
-#define TRX_CH_FLAG_AUTO (1 << 1)
-/* Is continuous burst transmission assumed */
-#define TRX_CH_FLAG_CBTX (1 << 2)
-
-#define MAX_A5_KEY_LEN (128 / 8)
-#define TRX_TS_COUNT 8
-
-/* Forward declaration to avoid mutual include */
-struct trx_lchan_state;
-struct trx_meas_set;
-struct trx_instance;
-struct trx_ts;
-
-enum trx_burst_type {
- TRX_BURST_GMSK,
- TRX_BURST_8PSK,
-};
-
-/**
- * These types define the different channels on a multiframe.
- * Each channel has queues and can be activated individually.
- */
-enum trx_lchan_type {
- TRXC_IDLE = 0,
- TRXC_FCCH,
- TRXC_SCH,
- TRXC_BCCH,
- TRXC_RACH,
- TRXC_CCCH,
- TRXC_TCHF,
- TRXC_TCHH_0,
- TRXC_TCHH_1,
- TRXC_SDCCH4_0,
- TRXC_SDCCH4_1,
- TRXC_SDCCH4_2,
- TRXC_SDCCH4_3,
- TRXC_SDCCH8_0,
- TRXC_SDCCH8_1,
- TRXC_SDCCH8_2,
- TRXC_SDCCH8_3,
- TRXC_SDCCH8_4,
- TRXC_SDCCH8_5,
- TRXC_SDCCH8_6,
- TRXC_SDCCH8_7,
- TRXC_SACCHTF,
- TRXC_SACCHTH_0,
- TRXC_SACCHTH_1,
- TRXC_SACCH4_0,
- TRXC_SACCH4_1,
- TRXC_SACCH4_2,
- TRXC_SACCH4_3,
- TRXC_SACCH8_0,
- TRXC_SACCH8_1,
- TRXC_SACCH8_2,
- TRXC_SACCH8_3,
- TRXC_SACCH8_4,
- TRXC_SACCH8_5,
- TRXC_SACCH8_6,
- TRXC_SACCH8_7,
- TRXC_PDTCH,
- TRXC_PTCCH,
- TRXC_SDCCH4_CBCH,
- TRXC_SDCCH8_CBCH,
- _TRX_CHAN_MAX
-};
-
-/* Represents a burst to be transmitted */
-struct sched_burst_req {
- uint32_t fn;
- uint8_t tn;
- uint8_t pwr;
-
- /* Internally used by the scheduler */
- uint8_t bid;
-
- ubit_t burst[EDGE_BURST_LEN];
- size_t burst_len;
-};
-
-typedef int trx_lchan_rx_func(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, uint8_t bid, const sbit_t *bits,
- const struct trx_meas_set *meas);
-
-typedef int trx_lchan_tx_func(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
-
-struct trx_lchan_desc {
- /*! \brief Human-readable name */
- const char *name;
- /*! \brief Human-readable description */
- const char *desc;
-
- /*! \brief Channel Number (like in RSL) */
- uint8_t chan_nr;
- /*! \brief Link ID (like in RSL) */
- uint8_t link_id;
- /*! \brief Sub-slot number (for SDCCH and TCH/H) */
- uint8_t ss_nr;
- /*! \brief GSMTAP channel type (see GSMTAP_CHANNEL_*) */
- uint8_t gsmtap_chan_type;
-
- /*! \brief How much memory do we need to store bursts */
- size_t burst_buf_size;
- /*! \brief Channel specific flags */
- uint8_t flags;
-
- /*! \brief Function to call when burst received from PHY */
- trx_lchan_rx_func *rx_fn;
- /*! \brief Function to call when data received from L2 */
- trx_lchan_tx_func *tx_fn;
-};
-
-struct trx_frame {
- /*! \brief Downlink TRX channel type */
- enum trx_lchan_type dl_chan;
- /*! \brief Downlink block ID */
- uint8_t dl_bid;
- /*! \brief Uplink TRX channel type */
- enum trx_lchan_type ul_chan;
- /*! \brief Uplink block ID */
- uint8_t ul_bid;
-};
-
-struct trx_multiframe {
- /*! \brief Channel combination */
- enum gsm_phys_chan_config chan_config;
- /*! \brief Human-readable name */
- const char *name;
- /*! \brief Repeats how many frames */
- uint8_t period;
- /*! \brief Applies to which timeslots */
- uint8_t slotmask;
- /*! \brief Contains which lchans */
- uint64_t lchan_mask;
- /*! \brief Pointer to scheduling structure */
- const struct trx_frame *frames;
-};
-
-struct trx_meas_set {
- /*! \brief TDMA frame number of the first burst this set belongs to */
- uint32_t fn;
- /*! \brief ToA256 (Timing of Arrival, 1/256 of a symbol) */
- int16_t toa256;
- /*! \brief RSSI (Received Signal Strength Indication) */
- int8_t rssi;
-};
-
-/* Simple ring buffer (up to 8 unique measurements) */
-struct trx_lchan_meas_hist {
- struct trx_meas_set buf[8];
- struct trx_meas_set *head;
-};
-
-/* States each channel on a multiframe */
-struct trx_lchan_state {
- /*! \brief Channel type */
- enum trx_lchan_type type;
- /*! \brief Channel status */
- uint8_t active;
- /*! \brief Link to a list of channels */
- struct llist_head list;
-
- /*! \brief Burst type: GMSK or 8PSK */
- enum trx_burst_type burst_type;
- /*! \brief Mask of received bursts */
- uint8_t rx_burst_mask;
- /*! \brief Mask of transmitted bursts */
- uint8_t tx_burst_mask;
- /*! \brief Burst buffer for RX */
- sbit_t *rx_bursts;
- /*! \brief Burst buffer for TX */
- ubit_t *tx_bursts;
-
- /*! \brief A primitive being sent */
- struct trx_ts_prim *prim;
-
- /*! \brief Mode for TCH channels (see GSM48_CMODE_*) */
- uint8_t tch_mode;
-
- /*! \brief FACCH/H on downlink */
- bool dl_ongoing_facch;
- /*! \brief pending FACCH/H blocks on Uplink */
- uint8_t ul_facch_blocks;
-
- /*! \brief Downlink measurements history */
- struct trx_lchan_meas_hist meas_hist;
- /*! \brief AVG measurements of the last received block */
- struct trx_meas_set meas_avg;
-
- /*! \brief TDMA loss detection state */
- struct {
- /*! \brief Last processed TDMA frame number */
- uint32_t last_proc;
- /*! \brief Number of processed TDMA frames */
- unsigned long num_proc;
- /*! \brief Number of lost TDMA frames */
- unsigned long num_lost;
- } tdma;
-
- /*! \brief SACCH state */
- struct {
- /*! \brief Cached measurement report (last received) */
- uint8_t mr_cache[GSM_MACBLOCK_LEN];
- /*! \brief Cache usage counter */
- uint8_t mr_cache_usage;
- /*! \brief Was a MR transmitted last time? */
- bool mr_tx_last;
- } sacch;
-
- /* AMR specific */
- struct {
- /*! \brief 4 possible codecs for AMR */
- uint8_t codec[4];
- /*! \brief Number of possible codecs */
- uint8_t codecs;
- /*! \brief Current uplink FT index */
- uint8_t ul_ft;
- /*! \brief Current downlink FT index */
- uint8_t dl_ft;
- /*! \brief Current uplink CMR index */
- uint8_t ul_cmr;
- /*! \brief Current downlink CMR index */
- uint8_t dl_cmr;
- /*! \brief If AMR loop is enabled */
- uint8_t amr_loop;
- /*! \brief Number of bit error rates */
- uint8_t ber_num;
- /*! \brief Sum of bit error rates */
- float ber_sum;
- } amr;
-
- /*! \brief A5/X encryption state */
- struct {
- uint8_t key[MAX_A5_KEY_LEN];
- uint8_t key_len;
- uint8_t algo;
- } a5;
-
- /* TS that this lchan belongs to */
- struct trx_ts *ts;
-};
-
-struct trx_ts {
- /*! \brief Timeslot index within a frame (0..7) */
- uint8_t index;
-
- /*! \brief Pointer to multiframe layout */
- const struct trx_multiframe *mf_layout;
- /*! \brief Channel states for logical channels */
- struct llist_head lchans;
- /*! \brief Queue primitives for TX */
- struct llist_head tx_prims;
- /* backpointer to its TRX */
- struct trx_instance *trx;
-};
-
-/* Represents one TX primitive in the queue of trx_ts */
-struct trx_ts_prim {
- /*! \brief Link to queue of TS */
- struct llist_head list;
- /*! \brief Logical channel type */
- enum trx_lchan_type chan;
- /*! \brief Payload length */
- size_t payload_len;
- /*! \brief Payload */
- uint8_t payload[0];
-};
-
-extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX];
-const struct trx_multiframe *sched_mframe_layout(
- enum gsm_phys_chan_config config, int tn);
-
-/* Scheduler management functions */
-int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance);
-int sched_trx_reset(struct trx_instance *trx, bool reset_clock);
-int sched_trx_shutdown(struct trx_instance *trx);
-
-/* Timeslot management functions */
-struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn);
-void sched_trx_del_ts(struct trx_instance *trx, int tn);
-int sched_trx_reset_ts(struct trx_instance *trx, int tn);
-int sched_trx_configure_ts(struct trx_instance *trx, int tn,
- enum gsm_phys_chan_config config);
-int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo,
- uint8_t *key, uint8_t key_len);
-
-/* Logical channel management functions */
-enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr);
-enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr,
- uint8_t link_id);
-
-void sched_trx_deactivate_all_lchans(struct trx_ts *ts);
-int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode);
-int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan);
-int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan);
-struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts,
- enum trx_lchan_type chan);
-
-/* Primitive management functions */
-int sched_prim_init(void *ctx, struct trx_ts_prim **prim,
- size_t pl_len, uint8_t chan_nr, uint8_t link_id);
-int sched_prim_push(struct trx_instance *trx,
- struct trx_ts_prim *prim, uint8_t chan_nr);
-
-#define TCH_MODE_IS_SPEECH(mode) \
- (mode == GSM48_CMODE_SPEECH_V1 \
- || mode == GSM48_CMODE_SPEECH_EFR \
- || mode == GSM48_CMODE_SPEECH_AMR)
-
-#define TCH_MODE_IS_DATA(mode) \
- (mode == GSM48_CMODE_DATA_14k5 \
- || mode == GSM48_CMODE_DATA_12k0 \
- || mode == GSM48_CMODE_DATA_6k0 \
- || mode == GSM48_CMODE_DATA_3k6)
-
-#define CHAN_IS_TCH(chan) \
- (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1)
-
-#define CHAN_IS_SACCH(chan) \
- (trx_lchan_desc[chan].link_id & TRX_CH_LID_SACCH)
-
-/* FIXME: we need a better way to identify / distinguish primitives */
-#define PRIM_IS_RACH11(prim) \
- (prim->payload_len == sizeof(struct l1ctl_ext_rach_req))
-
-#define PRIM_IS_RACH8(prim) \
- (prim->payload_len == sizeof(struct l1ctl_rach_req))
-
-#define PRIM_IS_RACH(prim) \
- (PRIM_IS_RACH8(prim) || PRIM_IS_RACH11(prim))
-
-#define PRIM_IS_TCH(prim) \
- (CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN)
-
-#define PRIM_IS_FACCH(prim) \
- (CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN)
-
-struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue,
- uint32_t fn, struct trx_lchan_state *lchan);
-int sched_prim_dummy(struct trx_lchan_state *lchan);
-void sched_prim_drop(struct trx_lchan_state *lchan);
-void sched_prim_flush_queue(struct llist_head *list);
-
-int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t fn, sbit_t *bits, uint16_t nbits,
- const struct trx_meas_set *meas);
-
-/* Shared declarations for lchan handlers */
-extern const uint8_t sched_nb_training_bits[8][26];
-
-const char *burst_mask2str(const uint8_t *mask, int bits);
-size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan);
-int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
- int bit_error_count, bool dec_failed, bool traffic);
-int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, bool traffic);
-int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn,
- uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr,
- const uint8_t *data, size_t data_len);
-
-/* Interleaved TCH/H block TDMA frame mapping */
-uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
- uint32_t last_fn, bool facch);
-bool sched_tchh_block_map_fn(enum trx_lchan_type chan,
- uint32_t fn, bool ul, bool facch, bool start);
-
-#define sched_tchh_traffic_start(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 0, 1)
-#define sched_tchh_traffic_end(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 0, 0)
-
-#define sched_tchh_facch_start(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
-#define sched_tchh_facch_end(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
-
-/* Measurement history */
-void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas);
-void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n);
diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h
deleted file mode 100644
index 43127cc1..00000000
--- a/src/host/trxcon/scheduler.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <time.h>
-
-#include <osmocom/core/timer.h>
-#include <osmocom/gsm/gsm0502.h>
-
-enum tdma_sched_clck_state {
- SCH_CLCK_STATE_WAIT,
- SCH_CLCK_STATE_OK,
-};
-
-/* Forward structure declaration */
-struct trx_sched;
-
-/*! \brief One scheduler instance */
-struct trx_sched {
- /*! \brief Clock state */
- enum tdma_sched_clck_state state;
- /*! \brief Local clock source */
- struct timespec clock;
- /*! \brief Count of processed frames */
- uint32_t fn_counter_proc;
- /*! \brief Local frame counter advance */
- uint32_t fn_counter_advance;
- /*! \brief Count of lost frames */
- uint32_t fn_counter_lost;
- /*! \brief Frame callback timer */
- struct osmo_timer_list clock_timer;
- /*! \brief Frame callback */
- void (*clock_cb)(struct trx_sched *sched);
- /*! \brief Private data (e.g. pointer to trx instance) */
- void *data;
-};
-
-int sched_clck_handle(struct trx_sched *sched, uint32_t fn);
-void sched_clck_reset(struct trx_sched *sched);
diff --git a/src/host/trxcon/src/Makefile.am b/src/host/trxcon/src/Makefile.am
new file mode 100644
index 00000000..7be7de62
--- /dev/null
+++ b/src/host/trxcon/src/Makefile.am
@@ -0,0 +1,64 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOCODING_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(NULL)
+
+
+noinst_LTLIBRARIES = libl1sched.la
+
+libl1sched_la_SOURCES = \
+ sched_lchan_common.c \
+ sched_lchan_pdtch.c \
+ sched_lchan_desc.c \
+ sched_lchan_xcch.c \
+ sched_lchan_tchf.c \
+ sched_lchan_tchh.c \
+ sched_lchan_rach.c \
+ sched_lchan_sch.c \
+ sched_mframe.c \
+ sched_prim.c \
+ sched_trx.c \
+ $(NULL)
+
+
+noinst_LTLIBRARIES += libl1gprs.la
+
+libl1gprs_la_SOURCES = \
+ l1gprs.c \
+ $(NULL)
+
+
+noinst_LTLIBRARIES += libtrxcon.la
+
+libtrxcon_la_SOURCES = \
+ trxcon_inst.c \
+ trxcon_fsm.c \
+ trxcon_shim.c \
+ l1ctl.c \
+ $(NULL)
+
+
+bin_PROGRAMS = trxcon
+
+trxcon_SOURCES = \
+ l1ctl_server.c \
+ trxcon_main.c \
+ logging.c \
+ trx_if.c \
+ $(NULL)
+
+trxcon_LDADD = \
+ libtrxcon.la \
+ libl1sched.la \
+ libl1gprs.la \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOCODING_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c
new file mode 100644
index 00000000..0f6bd1be
--- /dev/null
+++ b/src/host/trxcon/src/l1ctl.c
@@ -0,0 +1,849 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * GSM L1 control interface handlers
+ *
+ * (C) 2014 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.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_58.h>
+
+#include <osmocom/bb/l1ctl_proto.h>
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+
+#define L1CTL_LENGTH 512
+#define L1CTL_HEADROOM 32
+
+/* Logging categories configurable via trxcon_set_log_cfg() */
+int g_logc_l1c = DLGLOBAL;
+int g_logc_l1d = DLGLOBAL;
+
+static const struct value_string l1ctl_ccch_mode_names[] = {
+ { CCCH_MODE_NONE, "NONE" },
+ { CCCH_MODE_NON_COMBINED, "NON_COMBINED" },
+ { CCCH_MODE_COMBINED, "COMBINED" },
+ { CCCH_MODE_COMBINED_CBCH, "COMBINED_CBCH" },
+ { 0, NULL },
+};
+
+static const struct value_string l1ctl_reset_names[] = {
+ { L1CTL_RES_T_BOOT, "BOOT" },
+ { L1CTL_RES_T_FULL, "FULL" },
+ { L1CTL_RES_T_SCHED, "SCHED" },
+ { 0, NULL },
+};
+
+static const char *arfcn2band_name(uint16_t arfcn)
+{
+ enum gsm_band band;
+
+ if (gsm_arfcn2band_rc(arfcn, &band) < 0)
+ return "(invalid)";
+
+ return gsm_band_name(band);
+}
+
+static struct msgb *l1ctl_alloc_msg(uint8_t msg_type)
+{
+ struct l1ctl_hdr *l1h;
+ struct msgb *msg;
+
+ /**
+ * Each L1CTL message gets its own length pushed in front
+ * before sending. This is why we need this small headroom.
+ */
+ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
+ L1CTL_HEADROOM, "l1ctl_tx_msg");
+ if (!msg) {
+ LOGP(g_logc_l1c, LOGL_ERROR, "Failed to allocate memory\n");
+ return NULL;
+ }
+
+ msg->l1h = msgb_put(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = msg_type;
+
+ return msg;
+}
+
+int l1ctl_tx_pm_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, int dbm, int last)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_pm_conf *pmc;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_PM_CONF);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG,
+ "Send PM Conf (%s %d = %d dBm)\n",
+ arfcn2band_name(band_arfcn),
+ band_arfcn & ~ARFCN_FLAG_MASK, dbm);
+
+ pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
+ pmc->band_arfcn = htons(band_arfcn);
+ pmc->pm[0] = dbm2rxlev(dbm);
+ pmc->pm[1] = 0;
+
+ if (last) {
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->flags |= L1CTL_F_DONE;
+ }
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_reset_ind(struct trxcon_inst *trxcon, uint8_t type)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = l1ctl_alloc_msg(L1CTL_RESET_IND);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send Reset Ind (%s)\n",
+ get_value_string(l1ctl_reset_names, type));
+
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_reset_conf(struct trxcon_inst *trxcon, uint8_t type)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = l1ctl_alloc_msg(L1CTL_RESET_CONF);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send Reset Conf (%s)\n",
+ get_value_string(l1ctl_reset_names, type));
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static struct l1ctl_info_dl *put_dl_info_hdr(struct msgb *msg,
+ const struct l1ctl_info_dl *dl_info)
+{
+ size_t len = sizeof(struct l1ctl_info_dl);
+ struct l1ctl_info_dl *dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
+
+ if (dl_info) /* Copy DL info provided by handler */
+ memcpy(dl, dl_info, len);
+ else /* Init DL info header */
+ memset(dl, 0x00, len);
+
+ return dl;
+}
+
+/* Fill in FBSB payload: BSIC and sync result */
+static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result, uint8_t bsic)
+{
+ struct l1ctl_fbsb_conf *conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf));
+
+ conf->result = result;
+ conf->bsic = bsic;
+
+ return conf;
+}
+
+int l1ctl_tx_fbsb_fail(struct trxcon_inst *trxcon, uint16_t band_arfcn)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_info_dl *dl;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ dl = put_dl_info_hdr(msg, NULL);
+
+ /* Fill in current ARFCN */
+ dl->band_arfcn = htons(band_arfcn);
+
+ fbsb_conf_make(msg, 255, 0);
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send FBSB Conf (timeout)\n");
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_fbsb_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, uint8_t bsic)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_fbsb_conf *conf;
+ struct l1ctl_info_dl *dl;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ dl = put_dl_info_hdr(msg, NULL);
+
+ /* Fill in current ARFCN */
+ dl->band_arfcn = htons(band_arfcn);
+
+ conf = fbsb_conf_make(msg, 0, bsic);
+
+ /* FIXME: set proper value */
+ conf->initial_freq_err = 0;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG,
+ "Send FBSB Conf (result=%u, bsic=%u)\n",
+ conf->result, conf->bsic);
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_ccch_mode_conf(struct trxcon_inst *trxcon, uint8_t mode)
+{
+ struct l1ctl_ccch_mode_conf *conf;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf));
+ conf->ccch_mode = mode;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+/**
+ * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND.
+ */
+int l1ctl_tx_dt_ind(struct trxcon_inst *trxcon,
+ const struct trxcon_param_rx_data_ind *ind)
+{
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(ind->traffic ? L1CTL_TRAFFIC_IND : L1CTL_DATA_IND);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ const struct l1ctl_info_dl dl_hdr = {
+ .chan_nr = ind->chan_nr,
+ .link_id = ind->link_id,
+ .frame_nr = htonl(ind->frame_nr),
+ .band_arfcn = htons(ind->band_arfcn),
+ .fire_crc = ind->data_len > 0 ? 0 : 2,
+ .rx_level = dbm2rxlev(ind->rssi),
+ .num_biterr = ind->n_errors,
+ /* TODO: set proper .snr */
+ };
+
+ put_dl_info_hdr(msg, &dl_hdr);
+
+ /* Copy the L2 payload if preset */
+ if (ind->data && ind->data_len > 0)
+ memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len);
+
+ /* Put message to upper layers */
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_rach_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_access_burst_cnf *cnf)
+{
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_RACH_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ const struct l1ctl_info_dl dl_hdr = {
+ .band_arfcn = htons(cnf->band_arfcn),
+ .frame_nr = htonl(cnf->frame_nr),
+ };
+
+ put_dl_info_hdr(msg, &dl_hdr);
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+
+/**
+ * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF.
+ */
+int l1ctl_tx_dt_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_data_cnf *cnf)
+{
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(cnf->traffic ? L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ const struct l1ctl_info_dl dl_hdr = {
+ .chan_nr = cnf->chan_nr,
+ .link_id = cnf->link_id,
+ .frame_nr = htonl(cnf->frame_nr),
+ .band_arfcn = htons(cnf->band_arfcn),
+ };
+
+ /* Copy DL frame header from source message */
+ put_dl_info_hdr(msg, &dl_hdr);
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mode)
+{
+ switch (mode) {
+ /* TODO: distinguish extended BCCH */
+ case CCCH_MODE_NON_COMBINED:
+ case CCCH_MODE_NONE:
+ return GSM_PCHAN_CCCH;
+
+ case CCCH_MODE_COMBINED:
+ return GSM_PCHAN_CCCH_SDCCH4;
+ case CCCH_MODE_COMBINED_CBCH:
+ return GSM_PCHAN_CCCH_SDCCH4_CBCH;
+
+ default:
+ LOGP(g_logc_l1c, LOGL_NOTICE, "Undandled CCCH mode (%u), "
+ "assuming non-combined configuration\n", mode);
+ return GSM_PCHAN_CCCH;
+ }
+}
+
+static int l1ctl_rx_fbsb_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_fbsb_req *fbsb;
+ int rc = 0;
+
+ fbsb = (const struct l1ctl_fbsb_req *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*fbsb)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short FBSB Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ struct trxcon_param_fbsb_search_req req = {
+ .pchan_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode),
+ .timeout_fns = ntohs(fbsb->timeout),
+ .band_arfcn = ntohs(fbsb->band_arfcn),
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received FBSB request (%s %d, timeout %u TDMA FNs)\n",
+ arfcn2band_name(req.band_arfcn),
+ req.band_arfcn & ~ARFCN_FLAG_MASK,
+ req.timeout_fns);
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_FBSB_SEARCH_REQ, &req);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_pm_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_pm_req *pmr;
+ int rc = 0;
+
+ pmr = (const struct l1ctl_pm_req *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*pmr)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short PM Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ struct trxcon_param_full_power_scan_req req = {
+ .band_arfcn_start = ntohs(pmr->range.band_arfcn_from),
+ .band_arfcn_stop = ntohs(pmr->range.band_arfcn_to),
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received power measurement request (%s: %d -> %d)\n",
+ arfcn2band_name(req.band_arfcn_start),
+ req.band_arfcn_start & ~ARFCN_FLAG_MASK,
+ req.band_arfcn_stop & ~ARFCN_FLAG_MASK);
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_FULL_POWER_SCAN_REQ, &req);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_reset_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_reset *res;
+ int rc = 0;
+
+ res = (const struct l1ctl_reset *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*res)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short Reset Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received reset request (%s)\n",
+ get_value_string(l1ctl_reset_names, res->type));
+
+ switch (res->type) {
+ case L1CTL_RES_T_FULL:
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_RESET_FULL_REQ, NULL);
+ break;
+ case L1CTL_RES_T_SCHED:
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_RESET_SCHED_REQ, NULL);
+ break;
+ default:
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "Unknown L1CTL_RESET_REQ type\n");
+ goto exit;
+ }
+
+ /* Confirm */
+ rc = l1ctl_tx_reset_conf(trxcon, res->type);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_echo_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_hdr *l1h;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Recv Echo Req\n");
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Send Echo Conf\n");
+
+ /* Nothing to do, just send it back */
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = L1CTL_ECHO_CONF;
+ msg->data = msg->l1h;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static int l1ctl_rx_ccch_mode_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_ccch_mode_req *mode_req;
+ int rc;
+
+ mode_req = (const struct l1ctl_ccch_mode_req *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*mode_req)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short Reset Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Received CCCH mode request (%s)\n",
+ get_value_string(l1ctl_ccch_mode_names, mode_req->ccch_mode));
+
+ struct trxcon_param_set_ccch_tch_mode_req req = {
+ /* Choose corresponding channel combination */
+ .mode = l1ctl_ccch_mode2pchan_config(mode_req->ccch_mode),
+ };
+
+ rc = osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_CCCH_MODE_REQ, &req);
+ if (rc == 0 && req.applied)
+ l1ctl_tx_ccch_mode_conf(trxcon, mode_req->ccch_mode);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_rach_req(struct trxcon_inst *trxcon, struct msgb *msg, bool is_11bit)
+{
+ struct trxcon_param_tx_access_burst_req req;
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_info_ul *ul;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+
+ if (is_11bit) {
+ const struct l1ctl_ext_rach_req *rr = (void *)ul->payload;
+
+ req = (struct trxcon_param_tx_access_burst_req) {
+ .offset = ntohs(rr->offset),
+ .synch_seq = rr->synch_seq,
+ .ra = ntohs(rr->ra11),
+ .is_11bit = true,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received 11-bit RACH request "
+ "(offset=%u, synch_seq=%u, ra11=0x%02hx)\n",
+ req.offset, req.synch_seq, req.ra);
+ } else {
+ const struct l1ctl_rach_req *rr = (void *)ul->payload;
+
+ req = (struct trxcon_param_tx_access_burst_req) {
+ .offset = ntohs(rr->offset),
+ .ra = rr->ra,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received 8-bit RACH request "
+ "(offset=%u, ra=0x%02x)\n", req.offset, req.ra);
+ }
+
+ /* The controlling L1CTL side always does include the UL info header,
+ * but may leave it empty. We assume RACH is on TS0 in this case. */
+ if (ul->chan_nr == 0x00) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "The UL info header is empty, assuming RACH is on TS0\n");
+ req.chan_nr = RSL_CHAN_RACH;
+ req.link_id = 0x00;
+ } else {
+ req.chan_nr = ul->chan_nr;
+ req.link_id = ul->link_id;
+ }
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_ACCESS_BURST_REQ, &req);
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int l1ctl_proc_est_req_h0(struct osmo_fsm_inst *fi,
+ struct trxcon_param_dch_est_req *req,
+ const struct l1ctl_h0 *h)
+{
+ req->h0.band_arfcn = ntohs(h->band_arfcn);
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "L1CTL_DM_EST_REQ indicates single ARFCN %s %u\n",
+ arfcn2band_name(req->h0.band_arfcn),
+ req->h0.band_arfcn & ~ARFCN_FLAG_MASK);
+
+ return 0;
+}
+
+static int l1ctl_proc_est_req_h1(struct osmo_fsm_inst *fi,
+ struct trxcon_param_dch_est_req *req,
+ const struct l1ctl_h1 *h)
+{
+ unsigned int i;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "L1CTL_DM_EST_REQ indicates a Frequency "
+ "Hopping (hsn=%u, maio=%u, chans=%u) channel\n",
+ h->hsn, h->maio, h->n);
+
+ /* No channels?!? */
+ if (!h->n) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "No channels in mobile allocation?!?\n");
+ return -EINVAL;
+ } else if (h->n > ARRAY_SIZE(h->ma)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "More than 64 channels in mobile allocation?!?\n");
+ return -EINVAL;
+ }
+
+ /* Convert from network to host byte order */
+ for (i = 0; i < h->n; i++)
+ req->h1.ma[i] = ntohs(h->ma[i]);
+ req->h1.n = h->n;
+ req->h1.hsn = h->hsn;
+ req->h1.maio = h->maio;
+
+ return 0;
+}
+
+static int l1ctl_rx_dm_est_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_dm_est_req *est_req;
+ const struct l1ctl_info_ul *ul;
+ int rc;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ est_req = (const struct l1ctl_dm_est_req *)ul->payload;
+
+ struct trxcon_param_dch_est_req req = {
+ .chan_nr = ul->chan_nr,
+ .tch_mode = est_req->tch_mode,
+ .tsc = est_req->tsc,
+ .hopping = est_req->h,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received L1CTL_DM_EST_REQ "
+ "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=%s)\n",
+ req.chan_nr & 0x07, req.chan_nr, req.tsc,
+ gsm48_chan_mode_name(est_req->tch_mode));
+
+ /* Frequency hopping? */
+ if (est_req->h)
+ rc = l1ctl_proc_est_req_h1(fi, &req, &est_req->h1);
+ else /* Single ARFCN */
+ rc = l1ctl_proc_est_req_h0(fi, &req, &est_req->h0);
+ if (rc)
+ goto exit;
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_DCH_EST_REQ, &req);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_dm_rel_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ\n");
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_DCH_REL_REQ, NULL);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/**
+ * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ.
+ */
+static int l1ctl_rx_dt_req(struct trxcon_inst *trxcon, struct msgb *msg, bool traffic)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_info_ul *ul;
+
+ /* Extract UL frame header */
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ msg->l2h = (uint8_t *)ul->payload;
+
+ struct trxcon_param_tx_data_req req = {
+ .traffic = traffic,
+ .chan_nr = ul->chan_nr,
+ .link_id = ul->link_id & 0x40,
+ .data_len = msgb_l2len(msg),
+ .data = ul->payload,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1d, LOGL_DEBUG,
+ "Recv %s Req (chan_nr=0x%02x, link_id=0x%02x, len=%zu)\n",
+ traffic ? "TRAFFIC" : "DATA", req.chan_nr, req.link_id, req.data_len);
+
+ switch (fi->state) {
+ case TRXCON_ST_DEDICATED:
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_DATA_REQ, &req);
+ break;
+ default:
+ if (!traffic && req.link_id == 0x40) /* only for SACCH */
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_UPDATE_SACCH_CACHE_REQ, &req);
+ /* TODO: log an error about uhnandled DATA.req / TRAFFIC.req */
+ }
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int l1ctl_rx_param_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_par_req *par_req;
+ const struct l1ctl_info_ul *ul;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ par_req = (const struct l1ctl_par_req *)ul->payload;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received L1CTL_PARAM_REQ (ta=%d, tx_power=%u)\n",
+ par_req->ta, par_req->tx_power);
+
+ struct trxcon_param_set_phy_config_req req = {
+ .type = TRXCON_PHY_CFGT_TX_PARAMS,
+ .tx_params = {
+ .timing_advance = par_req->ta,
+ .tx_power = par_req->tx_power,
+ }
+ };
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int l1ctl_rx_tch_mode_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_tch_mode_req *mode_req;
+ int rc;
+
+ mode_req = (const struct l1ctl_tch_mode_req *)msg->l1h;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received L1CTL_TCH_MODE_REQ (tch_mode=%s, audio_mode=%u)\n",
+ gsm48_chan_mode_name(mode_req->tch_mode), mode_req->audio_mode);
+
+ /* TODO: do we need to care about audio_mode? */
+
+ struct trxcon_param_set_ccch_tch_mode_req req = {
+ .mode = mode_req->tch_mode,
+ };
+ if (mode_req->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ req.amr.start_codec = mode_req->amr.start_codec;
+ req.amr.codecs_bitmask = mode_req->amr.codecs_bitmask;
+ }
+
+ rc = osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_TCH_MODE_REQ, &req);
+ if (rc != 0 || !req.applied) {
+ talloc_free(msg);
+ return rc;
+ }
+
+ /* Re-use the original message as confirmation */
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ l1h->msg_type = L1CTL_TCH_MODE_CONF;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static int l1ctl_rx_crypto_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_crypto_req *cr;
+ const struct l1ctl_info_ul *ul;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ cr = (const struct l1ctl_crypto_req *)ul->payload;
+
+ struct trxcon_param_crypto_req req = {
+ .chan_nr = ul->chan_nr,
+ .a5_algo = cr->algo,
+ .key_len = cr->key_len,
+ .key = cr->key,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n",
+ req.a5_algo, req.key_len);
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_CRYPTO_REQ, &req);
+
+ msgb_free(msg);
+ return 0;
+}
+
+int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1ctl_hdr *l1h;
+ int rc;
+
+ l1h = (const struct l1ctl_hdr *)msg->l1h;
+ msg->l1h = (uint8_t *)l1h->data;
+
+ switch (l1h->msg_type) {
+ case L1CTL_FBSB_REQ:
+ return l1ctl_rx_fbsb_req(trxcon, msg);
+ case L1CTL_PM_REQ:
+ return l1ctl_rx_pm_req(trxcon, msg);
+ case L1CTL_RESET_REQ:
+ return l1ctl_rx_reset_req(trxcon, msg);
+ case L1CTL_ECHO_REQ:
+ return l1ctl_rx_echo_req(trxcon, msg);
+ case L1CTL_CCCH_MODE_REQ:
+ return l1ctl_rx_ccch_mode_req(trxcon, msg);
+ case L1CTL_RACH_REQ:
+ return l1ctl_rx_rach_req(trxcon, msg, false);
+ case L1CTL_EXT_RACH_REQ:
+ return l1ctl_rx_rach_req(trxcon, msg, true);
+ case L1CTL_DM_EST_REQ:
+ return l1ctl_rx_dm_est_req(trxcon, msg);
+ case L1CTL_DM_REL_REQ:
+ return l1ctl_rx_dm_rel_req(trxcon, msg);
+ case L1CTL_DATA_REQ:
+ return l1ctl_rx_dt_req(trxcon, msg, false);
+ case L1CTL_TRAFFIC_REQ:
+ return l1ctl_rx_dt_req(trxcon, msg, true);
+ case L1CTL_PARAM_REQ:
+ return l1ctl_rx_param_req(trxcon, msg);
+ case L1CTL_TCH_MODE_REQ:
+ return l1ctl_rx_tch_mode_req(trxcon, msg);
+ case L1CTL_CRYPTO_REQ:
+ return l1ctl_rx_crypto_req(trxcon, msg);
+ case L1CTL_GPRS_UL_TBF_CFG_REQ:
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_TBF_CFG_REQ, msg);
+ msgb_free(msg);
+ return rc;
+ case L1CTL_GPRS_DL_TBF_CFG_REQ:
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_DL_TBF_CFG_REQ, msg);
+ msgb_free(msg);
+ return rc;
+ case L1CTL_GPRS_UL_BLOCK_REQ:
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_BLOCK_REQ, msg);
+ msgb_free(msg);
+ return rc;
+ /* Not (yet) handled messages */
+ case L1CTL_NEIGH_PM_REQ:
+ case L1CTL_DM_FREQ_REQ:
+ case L1CTL_SIM_REQ:
+ LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_NOTICE,
+ "Ignoring unsupported message (type=%u)\n",
+ l1h->msg_type);
+ msgb_free(msg);
+ return -ENOTSUP;
+ default:
+ LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_ERROR, "Unknown MSG type %u: %s\n",
+ l1h->msg_type, osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+}
diff --git a/src/host/trxcon/src/l1ctl_server.c b/src/host/trxcon/src/l1ctl_server.c
new file mode 100644
index 00000000..c0f10158
--- /dev/null
+++ b/src/host/trxcon/src/l1ctl_server.c
@@ -0,0 +1,282 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * UNIX socket server for L1CTL
+ *
+ * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2022 by 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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/write_queue.h>
+
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+
+#define LOGP_CLI(cli, cat, level, fmt, args...) \
+ LOGP(cat, level, "%s" fmt, (cli)->log_prefix, ## args)
+
+static int l1ctl_client_read_cb(struct osmo_fd *ofd)
+{
+ struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
+ struct msgb *msg;
+ uint16_t len;
+ int rc;
+
+ /* Attempt to read from socket */
+ rc = read(ofd->fd, &len, L1CTL_MSG_LEN_FIELD);
+ if (rc != L1CTL_MSG_LEN_FIELD) {
+ if (rc <= 0) {
+ LOGP_CLI(client, DL1D, LOGL_NOTICE,
+ "L1CTL connection error: read() failed (rc=%d): %s\n",
+ rc, strerror(errno));
+ } else {
+ LOGP_CLI(client, DL1D, LOGL_NOTICE,
+ "L1CTL connection error: short read\n");
+ rc = -EIO;
+ }
+ l1ctl_client_conn_close(client);
+ return -EBADF; /* client fd is gone, avoid processing any other events. */
+ }
+
+ /* Check message length */
+ len = ntohs(len);
+ if (len > L1CTL_LENGTH) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
+ return -EINVAL;
+ }
+
+ /* Allocate a new msg */
+ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
+ L1CTL_HEADROOM, "l1ctl_rx_msg");
+ if (!msg) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to allocate msg\n");
+ return -ENOMEM;
+ }
+
+ msg->l1h = msgb_put(msg, len);
+ rc = read(ofd->fd, msg->l1h, msgb_l1len(msg));
+ if (rc != len) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR,
+ "Can not read data: len=%d < rc=%d: %s\n",
+ len, rc, strerror(errno));
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* Debug print */
+ LOGP_CLI(client, DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len));
+
+ /* Call L1CTL handler */
+ client->server->cfg->conn_read_cb(client, msg);
+
+ return 0;
+}
+
+static int l1ctl_client_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
+ int len;
+
+ if (ofd->fd <= 0)
+ return -EINVAL;
+
+ len = write(ofd->fd, msg->data, msg->len);
+ if (len != msg->len) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR,
+ "Failed to write data: written (%d) < msg_len (%d)\n",
+ len, msg->len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Connection handler */
+static int l1ctl_server_conn_cb(struct osmo_fd *sfd, unsigned int flags)
+{
+ struct l1ctl_server *server = (struct l1ctl_server *)sfd->data;
+ struct l1ctl_client *client;
+ int rc, client_fd;
+
+ client_fd = accept(sfd->fd, NULL, NULL);
+ if (client_fd < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to accept() a new connection: "
+ "%s\n", strerror(errno));
+ return client_fd;
+ }
+
+ if (server->cfg->num_clients_max > 0 /* 0 means unlimited */ &&
+ server->num_clients >= server->cfg->num_clients_max) {
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL server cannot accept more "
+ "than %u connection(s)\n", server->cfg->num_clients_max);
+ close(client_fd);
+ return -ENOMEM;
+ }
+
+ client = talloc_zero(server, struct l1ctl_client);
+ if (client == NULL) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate an L1CTL client\n");
+ close(client_fd);
+ return -ENOMEM;
+ }
+
+ /* Init the client's write queue */
+ osmo_wqueue_init(&client->wq, 100);
+ INIT_LLIST_HEAD(&client->wq.bfd.list);
+
+ client->wq.write_cb = &l1ctl_client_write_cb;
+ client->wq.read_cb = &l1ctl_client_read_cb;
+ osmo_fd_setup(&client->wq.bfd, client_fd, OSMO_FD_READ, &osmo_wqueue_bfd_cb, client, 0);
+
+ /* Register the client's write queue */
+ rc = osmo_fd_register(&client->wq.bfd);
+ if (rc != 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to register a new connection fd\n");
+ close(client->wq.bfd.fd);
+ talloc_free(client);
+ return rc;
+ }
+
+ llist_add_tail(&client->list, &server->clients);
+ client->id = server->next_client_id++;
+ client->server = server;
+ server->num_clients++;
+
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL server got a new connection (id=%u)\n", client->id);
+
+ if (client->server->cfg->conn_accept_cb != NULL)
+ client->server->cfg->conn_accept_cb(client);
+
+ return 0;
+}
+
+int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg)
+{
+ uint8_t *len;
+
+ /* Debug print */
+ LOGP_CLI(client, DL1D, LOGL_DEBUG, "TX: '%s'\n", osmo_hexdump(msg->data, msg->len));
+
+ if (msg->l1h != msg->data)
+ LOGP_CLI(client, DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
+
+ /* Prepend 16-bit length before sending */
+ len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
+ osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
+
+ if (osmo_wqueue_enqueue(&client->wq, msg) != 0) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void l1ctl_client_conn_close(struct l1ctl_client *client)
+{
+ struct l1ctl_server *server = client->server;
+
+ LOGP_CLI(client, DL1C, LOGL_NOTICE, "Closing L1CTL connection\n");
+
+ if (server->cfg->conn_close_cb != NULL)
+ server->cfg->conn_close_cb(client);
+
+ /* Close connection socket */
+ osmo_fd_unregister(&client->wq.bfd);
+ close(client->wq.bfd.fd);
+ client->wq.bfd.fd = -1;
+
+ /* Clear pending messages */
+ osmo_wqueue_clear(&client->wq);
+
+ client->server->num_clients--;
+ llist_del(&client->list);
+ talloc_free(client);
+
+ /* If this was the last client, reset the client IDs generator to 0.
+ * This way avoid assigning huge unreadable client IDs like 26545. */
+ if (llist_empty(&server->clients))
+ server->next_client_id = 0;
+}
+
+struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg)
+{
+ struct l1ctl_server *server;
+ int rc;
+
+ LOGP(DL1C, LOGL_NOTICE, "Init L1CTL server (sock_path=%s)\n", cfg->sock_path);
+
+ server = talloc(ctx, struct l1ctl_server);
+ OSMO_ASSERT(server != NULL);
+
+ *server = (struct l1ctl_server) {
+ .clients = LLIST_HEAD_INIT(server->clients),
+ .cfg = cfg,
+ };
+
+ /* conn_read_cb shall not be NULL */
+ OSMO_ASSERT(cfg->conn_read_cb != NULL);
+
+ /* Bind connection handler */
+ osmo_fd_setup(&server->ofd, -1, OSMO_FD_READ, &l1ctl_server_conn_cb, server, 0);
+
+ rc = osmo_sock_unix_init_ofd(&server->ofd, SOCK_STREAM, 0,
+ cfg->sock_path, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n",
+ strerror(errno));
+ talloc_free(server);
+ return NULL;
+ }
+
+ return server;
+}
+
+void l1ctl_server_free(struct l1ctl_server *server)
+{
+ LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL server\n");
+
+ /* Close all client connections */
+ while (!llist_empty(&server->clients)) {
+ struct l1ctl_client *client = llist_entry(server->clients.next,
+ struct l1ctl_client,
+ list);
+ l1ctl_client_conn_close(client);
+ }
+
+ /* Unbind listening socket */
+ if (server->ofd.fd != -1) {
+ osmo_fd_unregister(&server->ofd);
+ close(server->ofd.fd);
+ server->ofd.fd = -1;
+ }
+
+ talloc_free(server);
+}
diff --git a/src/host/trxcon/src/l1gprs.c b/src/host/trxcon/src/l1gprs.c
new file mode 120000
index 00000000..0185f68b
--- /dev/null
+++ b/src/host/trxcon/src/l1gprs.c
@@ -0,0 +1 @@
+../../../shared/l1gprs.c \ No newline at end of file
diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/src/logging.c
index 78915f21..e8730450 100644
--- a/src/host/trxcon/logging.c
+++ b/src/host/trxcon/src/logging.c
@@ -15,19 +15,16 @@
* 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 <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
-#include "logging.h"
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/logging.h>
-static struct log_info_cat trx_log_info_cat[] = {
+static struct log_info_cat trxcon_log_info_cat[] = {
[DAPP] = {
.name = "DAPP",
.description = "Application",
@@ -46,8 +43,8 @@ static struct log_info_cat trx_log_info_cat[] = {
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
- [DTRX] = {
- .name = "DTRX",
+ [DTRXC] = {
+ .name = "DTRXC",
.description = "Transceiver control interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
@@ -70,19 +67,37 @@ static struct log_info_cat trx_log_info_cat[] = {
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DGPRS] = {
+ .name = "DGPRS",
+ .description = "L1 GPRS (MAC layer)",
+ .color = "\033[1;36m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
-static const struct log_info trx_log_info = {
- .cat = trx_log_info_cat,
- .num_cat = ARRAY_SIZE(trx_log_info_cat),
+static const struct log_info trxcon_log_info = {
+ .cat = trxcon_log_info_cat,
+ .num_cat = ARRAY_SIZE(trxcon_log_info_cat),
};
-int trx_log_init(void *tall_ctx, const char *category_mask)
+static const int trxcon_log_cfg[] = {
+ [TRXCON_LOGC_FSM] = DAPP,
+ [TRXCON_LOGC_L1C] = DL1C,
+ [TRXCON_LOGC_L1D] = DL1D,
+ [TRXCON_LOGC_SCHC] = DSCH,
+ [TRXCON_LOGC_SCHD] = DSCHD,
+ [TRXCON_LOGC_GPRS] = DGPRS,
+};
+
+int trxcon_logging_init(void *tall_ctx, const char *category_mask)
{
- osmo_init_logging2(tall_ctx, &trx_log_info);
+ osmo_init_logging2(tall_ctx, &trxcon_log_info);
+ log_target_file_switch_to_wqueue(osmo_stderr_target);
if (category_mask)
log_parse_category_mask(osmo_stderr_target, category_mask);
+ trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
+
return 0;
}
diff --git a/src/host/trxcon/src/sched_lchan_common.c b/src/host/trxcon/src/sched_lchan_common.c
new file mode 100644
index 00000000..2b1729ae
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_common.c
@@ -0,0 +1,137 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: common routines for lchan handlers
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <talloc.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */
+const uint8_t l1sched_nb_training_bits[8][26] = {
+ {
+ 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1,
+ },
+ {
+ 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1,
+ 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
+ },
+ {
+ 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+ },
+ {
+ 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
+ 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
+ },
+ {
+ 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
+ 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
+ },
+ {
+ 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
+ },
+ {
+ 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
+ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
+ },
+ {
+ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,
+ 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
+ },
+};
+
+/* Get a string representation of the burst buffer's completeness.
+ * Examples: " ****.." (incomplete, 4/6 bursts)
+ * " ****" (complete, all 4 bursts)
+ * "**.***.." (incomplete, 5/8 bursts) */
+const char *l1sched_burst_mask2str(const uint32_t *mask, int bits)
+{
+ static char buf[32 + 1];
+ char *ptr = buf;
+
+ OSMO_ASSERT(bits <= 32 && bits > 0);
+
+ while (--bits >= 0)
+ *(ptr++) = (*mask & (1 << bits)) ? '*' : '.';
+ *ptr = '\0';
+
+ return buf;
+}
+
+bool l1sched_lchan_amr_prim_is_valid(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, bool is_cmr)
+{
+ enum osmo_amr_type ft_codec;
+ uint8_t cmr_codec;
+ int ft, cmr, len;
+
+ len = osmo_amr_rtp_dec(msgb_l2(msg), msgb_l2len(msg),
+ &cmr_codec, NULL, &ft_codec, NULL, NULL);
+ if (len < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ return false;
+ }
+ ft = -1;
+ cmr = -1;
+ for (unsigned int i = 0; i < lchan->amr.codecs; i++) {
+ if (lchan->amr.codec[i] == ft_codec)
+ ft = i;
+ if (lchan->amr.codec[i] == cmr_codec)
+ cmr = i;
+ }
+ if (ft < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Codec (FT = %d) of RTP frame not in list\n", ft_codec);
+ return false;
+ }
+ if (is_cmr && lchan->amr.ul_ft != ft) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Codec (FT = %d) of RTP cannot be changed now, but in next frame\n",
+ ft_codec);
+ return false;
+ }
+ lchan->amr.ul_ft = ft;
+ if (cmr < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Codec (CMR = %d) of RTP frame not in list\n", cmr_codec);
+ } else {
+ lchan->amr.ul_cmr = cmr;
+ }
+
+ return true;
+}
diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/src/sched_lchan_desc.c
index b6a72b39..db5446e3 100644
--- a/src/host/trxcon/sched_lchan_desc.c
+++ b/src/host/trxcon/src/sched_lchan_desc.c
@@ -5,6 +5,7 @@
* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
* (C) 2015 by Harald Welte <laforge@gnumonks.org>
+ * Contributions by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -24,114 +25,99 @@
*/
#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include <osmocom/core/gsmtap.h>
-#include "sched_trx.h"
+#include <osmocom/bb/l1sched/l1sched.h>
/* Forward declaration of handlers */
-int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas);
+int rx_data_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
+int tx_data_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas);
+int rx_sch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
+int tx_rach_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas);
+int rx_tchf_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
+int tx_tchf_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas);
+int rx_tchh_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
+int tx_tchh_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas);
+int rx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan,
- struct sched_burst_req *br);
+int tx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
- [TRXC_IDLE] = {
+const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
+ [L1SCHED_IDLE] = {
.name = "IDLE",
.desc = "Idle channel",
/* The MS needs to perform neighbour measurements during
* IDLE slots, however this is not implemented (yet). */
},
- [TRXC_FCCH] = {
+ [L1SCHED_FCCH] = {
.name = "FCCH", /* 3GPP TS 05.02, section 3.3.2.1 */
.desc = "Frequency correction channel",
/* Handled by transceiver, nothing to do. */
},
- [TRXC_SCH] = {
+ [L1SCHED_SCH] = {
.name = "SCH", /* 3GPP TS 05.02, section 3.3.2.2 */
.desc = "Synchronization channel",
/* 3GPP TS 05.03, section 4.7. Handled by transceiver,
* however we still need to parse BSIC (BCC / NCC). */
- .flags = TRX_CH_FLAG_AUTO,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_sch_fn,
},
- [TRXC_BCCH] = {
+ [L1SCHED_BCCH] = {
.name = "BCCH", /* 3GPP TS 05.02, section 3.3.2.3 */
.desc = "Broadcast control channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_BCCH,
.chan_nr = RSL_CHAN_BCCH,
/* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
* regular interleaving (3GPP TS 05.02, clause 7, table 3):
* a L2 frame is interleaved over 4 consecutive bursts. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_AUTO,
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_data_fn,
},
- [TRXC_RACH] = {
+ [L1SCHED_RACH] = {
.name = "RACH", /* 3GPP TS 05.02, section 3.3.3.1 */
.desc = "Random access channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_RACH,
.chan_nr = RSL_CHAN_RACH,
/* Tx only, RACH convolutional coding (3GPP TS 05.03, section 4.6). */
- .flags = TRX_CH_FLAG_AUTO,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.tx_fn = tx_rach_fn,
},
- [TRXC_CCCH] = {
+ [L1SCHED_CCCH] = {
.name = "CCCH", /* 3GPP TS 05.02, section 3.3.3.1 */
.desc = "Common control channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_CCCH,
.chan_nr = RSL_CHAN_PCH_AGCH,
/* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
* regular interleaving (3GPP TS 05.02, clause 7, table 3):
* a L2 frame is interleaved over 4 consecutive bursts. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_AUTO,
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_data_fn,
},
- [TRXC_TCHF] = {
+ [L1SCHED_TCHF] = {
.name = "TCH/F", /* 3GPP TS 05.02, section 3.2 */
.desc = "Full Rate traffic channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F,
.chan_nr = RSL_CHAN_Bm_ACCHs,
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
@@ -144,18 +130,15 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
*
* The MS shall continuously transmit bursts, even if there is nothing
* to send, unless DTX (Discontinuous Transmission) is used. */
- .burst_buf_size = 8 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_tchf_fn,
.tx_fn = tx_tchf_fn,
},
- [TRXC_TCHH_0] = {
+ [L1SCHED_TCHH_0] = {
.name = "TCH/H(0)", /* 3GPP TS 05.02, section 3.2 */
.desc = "Half Rate traffic channel (sub-channel 0)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H,
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 0,
+ .link_id = L1SCHED_CH_LID_DEDIC,
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
@@ -173,455 +156,365 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
*
* The MS shall continuously transmit bursts, even if there is nothing
* to send, unless DTX (Discontinuous Transmission) is used. */
- .burst_buf_size = 6 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_tchh_fn,
.tx_fn = tx_tchh_fn,
},
- [TRXC_TCHH_1] = {
+ [L1SCHED_TCHH_1] = {
.name = "TCH/H(1)", /* 3GPP TS 05.02, section 3.2 */
.desc = "Half Rate traffic channel (sub-channel 1)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H,
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 1,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_TCHH_0, see above. */
- .burst_buf_size = 6 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_TCHH_0, see above. */
+ .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_tchh_fn,
.tx_fn = tx_tchh_fn,
},
- [TRXC_SDCCH4_0] = {
+ [L1SCHED_SDCCH4_0] = {
.name = "SDCCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 0)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 0,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH4_1] = {
+ [L1SCHED_SDCCH4_1] = {
.name = "SDCCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 1)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 1,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH4_2] = {
+ [L1SCHED_SDCCH4_2] = {
.name = "SDCCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 2)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 2,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH4_3] = {
+ [L1SCHED_SDCCH4_3] = {
.name = "SDCCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 3)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 3,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_0] = {
+ [L1SCHED_SDCCH8_0] = {
.name = "SDCCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 0)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 0,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_1] = {
+ [L1SCHED_SDCCH8_1] = {
.name = "SDCCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 1)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 1,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_2] = {
+ [L1SCHED_SDCCH8_2] = {
.name = "SDCCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 2)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 2,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_3] = {
+ [L1SCHED_SDCCH8_3] = {
.name = "SDCCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 3)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 3,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_4] = {
+ [L1SCHED_SDCCH8_4] = {
.name = "SDCCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 4)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 4,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_5] = {
+ [L1SCHED_SDCCH8_5] = {
.name = "SDCCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 5)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 5,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_6] = {
+ [L1SCHED_SDCCH8_6] = {
.name = "SDCCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 6)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 6,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_7] = {
+ [L1SCHED_SDCCH8_7] = {
.name = "SDCCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 7)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
- .link_id = TRX_CH_LID_DEDIC,
- .ss_nr = 7,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCHTF] = {
+ [L1SCHED_SACCHTF] = {
.name = "SACCH/TF", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow TCH/F associated control channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_Bm_ACCHs,
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCHTH_0] = {
+ [L1SCHED_SACCHTH_0] = {
.name = "SACCH/TH(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow TCH/H associated control channel (sub-channel 0)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 0,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCHTH_1] = {
+ [L1SCHED_SACCHTH_1] = {
.name = "SACCH/TH(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow TCH/H associated control channel (sub-channel 1)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 1,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_0] = {
+ [L1SCHED_SACCH4_0] = {
.name = "SACCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 0)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 0,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_1] = {
+ [L1SCHED_SACCH4_1] = {
.name = "SACCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 1)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 1,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_2] = {
+ [L1SCHED_SACCH4_2] = {
.name = "SACCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 2)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 2,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_3] = {
+ [L1SCHED_SACCH4_3] = {
.name = "SACCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 3)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 3,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_0] = {
+ [L1SCHED_SACCH8_0] = {
.name = "SACCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 0)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 0,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_1] = {
+ [L1SCHED_SACCH8_1] = {
.name = "SACCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 1)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 1,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_2] = {
+ [L1SCHED_SACCH8_2] = {
.name = "SACCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 2)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 2,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_3] = {
+ [L1SCHED_SACCH8_3] = {
.name = "SACCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 3)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 3,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_4] = {
+ [L1SCHED_SACCH8_4] = {
.name = "SACCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 4)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 4,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_5] = {
+ [L1SCHED_SACCH8_5] = {
.name = "SACCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 5)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 5,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_6] = {
+ [L1SCHED_SACCH8_6] = {
.name = "SACCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 6)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 6,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_7] = {
+ [L1SCHED_SACCH8_7] = {
.name = "SACCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 7)",
- .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
- .link_id = TRX_CH_LID_SACCH,
- .ss_nr = 7,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_PDTCH] = {
+ [L1SCHED_PDTCH] = {
.name = "PDTCH", /* 3GPP TS 05.02, sections 3.2.4, 3.3.2.4 */
.desc = "Packet data traffic & control channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_PDTCH,
.chan_nr = RSL_CHAN_OSMO_PDCH,
/* Rx and Tx, multiple coding schemes: CS-1..4 and MCS-1..9 (3GPP TS
* 05.03, chapter 5), regular interleaving as specified for xCCH.
* NOTE: the burst buffer is three times bigger because the
* payload of EDGE bursts is three times longer. */
- .burst_buf_size = 3 * 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_PDCH,
+ .burst_buf_size = 4 * GSM_NBITS_NB_8PSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_PDCH,
.rx_fn = rx_pdtch_fn,
.tx_fn = tx_pdtch_fn,
},
- [TRXC_PTCCH] = {
+ [L1SCHED_PTCCH] = {
.name = "PTCCH", /* 3GPP TS 05.02, section 3.3.4.2 */
.desc = "Packet Timing advance control channel",
- .gsmtap_chan_type = GSMTAP_CHANNEL_PTCCH,
.chan_nr = RSL_CHAN_OSMO_PDCH,
- .link_id = TRX_CH_LID_PTCCH,
+ .link_id = L1SCHED_CH_LID_PTCCH,
/* On the Uplink, mobile stations transmit random Access Bursts
* to allow estimation of the timing advance for one MS in packet
* transfer mode. On Downlink, the network sends timing advance
* updates for several mobile stations. The coding scheme used
* for PTCCH/D messages is the same as for PDTCH CS-1. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_PDCH,
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_PDCH,
.rx_fn = rx_pdtch_fn,
.tx_fn = tx_rach_fn,
},
- [TRXC_SDCCH4_CBCH] = {
+ [L1SCHED_SDCCH4_CBCH] = {
.name = "SDCCH/4(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
.desc = "Cell Broadcast channel on SDCCH/4",
- .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH51,
.chan_nr = RSL_CHAN_OSMO_CBCH4,
- .ss_nr = 2,
- /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_AUTO,
+ /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_data_fn,
},
- [TRXC_SDCCH8_CBCH] = {
+ [L1SCHED_SDCCH8_CBCH] = {
.name = "SDCCH/8(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
.desc = "Cell Broadcast channel on SDCCH/8",
- .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH52,
.chan_nr = RSL_CHAN_OSMO_CBCH8,
- .ss_nr = 2,
- /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
},
};
diff --git a/src/host/trxcon/src/sched_lchan_pdtch.c b/src/host/trxcon/src/sched_lchan_pdtch.c
new file mode 100644
index 00000000..5b884ddc
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_pdtch.c
@@ -0,0 +1,195 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2018-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+int rx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ uint8_t l2[GPRS_L2_MAX_LEN];
+ int n_errors, n_bits_total, rc;
+ sbit_t *bursts_p, *burst;
+ size_t l2_len;
+ uint32_t *mask;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Packet data received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bi->bid != 0)
+ return 0;
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to buffer of 4 bursts */
+ burst = bursts_p + bi->bid * 116;
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until complete set of bursts */
+ if (bi->bid != 3)
+ return 0;
+
+ /* Calculate AVG of the measurements */
+ l1sched_lchan_meas_avg(lchan, 4);
+
+ /* Check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received incomplete (%s) packet data at fn=%u (%u/%u)\n",
+ l1sched_burst_mask2str(mask, 4), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % lchan->ts->mf_layout->period,
+ lchan->ts->mf_layout->period);
+ /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
+ }
+
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
+ /* Attempt to decode */
+ rc = gsm0503_pdtch_decode(l2, bursts_p,
+ NULL, &n_errors, &n_bits_total);
+ if (rc < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+ }
+
+ /* Determine L2 length */
+ l2_len = rc > 0 ? rc : 0;
+
+ /* Send a L2 frame to the higher layers */
+ l1sched_lchan_emit_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, true);
+
+ return 0;
+}
+
+static struct msgb *prim_dequeue_pdtch(struct l1sched_lchan_state *lchan, uint32_t fn)
+{
+ while (!llist_empty(&lchan->tx_prims)) {
+ struct msgb *msg = llist_first_entry(&lchan->tx_prims, struct msgb, list);
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ int ret = gsm0502_fncmp(prim->data_req.frame_nr, fn);
+
+ if (OSMO_LIKELY(ret == 0)) { /* it's a match! */
+ llist_del(&msg->list);
+ return msg;
+ } else if (ret > 0) { /* not now, come back later */
+ break;
+ } /* else: the ship has sailed, drop your ticket */
+
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "%s(): dropping stale Tx prim (current Fn=%u, prim Fn=%u): %s\n",
+ __func__, fn, prim->data_req.frame_nr, msgb_hexdump_l2(msg));
+ llist_del(&msg->list);
+ msgb_free(msg);
+ }
+
+ return NULL;
+}
+
+int tx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ *mask = *mask << 4;
+
+ struct msgb *msg = prim_dequeue_pdtch(lchan, br->fn);
+ if (msg == NULL)
+ return -ENOENT;
+
+ /* Encode payload */
+ rc = gsm0503_pdtch_encode(bursts_p, msgb_l2(msg), msgb_l2len(msg));
+ if (rc < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ /* Cache the prim, so that we can confirm it later (see below) */
+ OSMO_ASSERT(lchan->prim == NULL);
+ lchan->prim = msg;
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = bursts_p + br->bid * 116;
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled at fn=%u burst=%u\n", br->fn, br->bid);
+
+ if (br->bid == 3) {
+ /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, lchan->prim,
+ GSM_TDMA_FN_SUB(br->fn, 3));
+ lchan->prim = NULL;
+ }
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_lchan_rach.c b/src/host/trxcon/src/sched_lchan_rach.c
new file mode 100644
index 00000000..905f1d57
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_rach.c
@@ -0,0 +1,136 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */
+#define RACH_EXT_TAIL_BITS_LEN 8
+#define RACH_SYNCH_SEQ_LEN 41
+#define RACH_PAYLOAD_LEN 36
+
+/* Extended tail bits (BN0..BN7) */
+static const ubit_t rach_ext_tail_bits[] = {
+ 0, 0, 1, 1, 1, 0, 1, 0,
+};
+
+/* Synchronization (training) sequence types */
+enum rach_synch_seq_t {
+ RACH_SYNCH_SEQ_UNKNOWN = -1,
+ RACH_SYNCH_SEQ_TS0, /* GSM, GMSK (default) */
+ RACH_SYNCH_SEQ_TS1, /* EGPRS, 8-PSK */
+ RACH_SYNCH_SEQ_TS2, /* EGPRS, GMSK */
+ RACH_SYNCH_SEQ_NUM
+};
+
+/* Synchronization (training) sequence bits */
+static const char rach_synch_seq_bits[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = {
+ [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000",
+ [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101",
+ [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111",
+};
+
+/* Synchronization (training) sequence names */
+static struct value_string rach_synch_seq_names[] = {
+ { RACH_SYNCH_SEQ_UNKNOWN, "UNKNOWN" },
+ { RACH_SYNCH_SEQ_TS0, "TS0: GSM, GMSK" },
+ { RACH_SYNCH_SEQ_TS1, "TS1: EGPRS, 8-PSK" },
+ { RACH_SYNCH_SEQ_TS2, "TS2: EGPRS, GMSK" },
+ { 0, NULL },
+};
+
+/* Obtain a to-be-transmitted RACH burst */
+int tx_rach_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ const uint8_t bsic = lchan->ts->sched->bsic;
+ uint8_t *burst_ptr = br->burst;
+ uint8_t payload[36];
+ int i, rc;
+
+ if (llist_empty(&lchan->tx_prims))
+ return 0;
+
+ struct msgb *msg = llist_first_entry(&lchan->tx_prims, struct msgb, list);
+ struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+
+ /* Delay sending according to offset value */
+ if (prim->rach_req.offset-- > 0)
+ return 0;
+ llist_del(&msg->list);
+
+ /* Check requested synch. sequence */
+ if (prim->rach_req.synch_seq >= RACH_SYNCH_SEQ_NUM) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Unknown RACH synch. sequence=0x%02x\n",
+ prim->rach_req.synch_seq);
+ msgb_free(msg);
+ return -ENOTSUP;
+ }
+
+ /* Encode the payload */
+ rc = gsm0503_rach_ext_encode(payload, prim->rach_req.ra,
+ bsic, prim->rach_req.is_11bit);
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Could not encode %s-bit RACH burst (ra=%u bsic=%u)\n",
+ prim->rach_req.is_11bit ? "11" : "8",
+ prim->rach_req.ra, bsic);
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* BN0-7: extended tail bits */
+ memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
+ burst_ptr += RACH_EXT_TAIL_BITS_LEN;
+
+ /* BN8-48: chosen synch. (training) sequence */
+ for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++)
+ *(burst_ptr++) = rach_synch_seq_bits[prim->rach_req.synch_seq][i] == '1';
+
+ /* BN49-84: encrypted bits (the payload) */
+ memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
+ burst_ptr += RACH_PAYLOAD_LEN;
+
+ /* BN85-156: tail bits & extended guard period */
+ memset(burst_ptr, 0, br->burst + GSM_NBITS_NB_GMSK_BURST - burst_ptr);
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_NOTICE, "Scheduled %s-bit RACH (%s) at fn=%u\n",
+ prim->rach_req.is_11bit ? "11" : "8",
+ get_value_string(rach_synch_seq_names, prim->rach_req.synch_seq), br->fn);
+
+ /* Confirm RACH request (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+
+ return 0;
+}
diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/src/sched_lchan_sch.c
index 18d4c58d..e2420050 100644
--- a/src/host/trxcon/sched_lchan_sch.c
+++ b/src/host/trxcon/src/sched_lchan_sch.c
@@ -2,7 +2,8 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -16,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -35,12 +32,8 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/coding/gsm0503_coding.h>
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
{
@@ -68,9 +61,23 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
time->fn = gsm_gsmtime2fn(time);
}
-int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- const sbit_t *bits, const struct trx_meas_set *meas)
+static int handle_sch_ind(struct l1sched_state *sched, uint32_t fn, uint8_t bsic)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->sch_ind.frame_nr = fn;
+ prim->sch_ind.bsic = bsic;
+
+ return l1sched_prim_to_user(sched, msg);
+}
+
+int rx_sch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
{
sbit_t payload[2 * 39];
struct gsm_time time;
@@ -79,55 +86,34 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
int rc;
/* Obtain payload from burst */
- memcpy(payload, bits + 3, 39);
- memcpy(payload + 39, bits + 3 + 39 + 64, 39);
+ memcpy(payload, bi->burst + 3, 39);
+ memcpy(payload + 39, bi->burst + 3 + 39 + 64, 39);
/* Attempt to decode */
rc = gsm0503_sch_decode(sb_info, payload);
if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad SCH burst at fn=%u\n", fn);
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad SCH burst at fn=%u\n", bi->fn);
return rc;
}
/* Decode BSIC and TDMA frame number */
decode_sb(&time, &bsic, sb_info);
- LOGP(DSCHD, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n",
- bsic, time.fn, trx->sched.fn_counter_proc);
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n",
+ bsic, time.fn, bi->fn);
/* Check if decoded frame number matches */
- if (time.fn != fn) {
- LOGP(DSCHD, LOGL_ERROR, "Decoded fn=%u does not match "
- "fn=%u provided by scheduler\n", time.fn, fn);
+ if (time.fn != bi->fn) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Decoded fn=%u does not match sched_fn=%u\n",
+ time.fn, bi->fn);
return -EINVAL;
}
- /* We don't need to send L1CTL_FBSB_CONF */
- if (trx->l1l->fbsb_conf_sent)
- return 0;
-
- /* Send L1CTL_FBSB_CONF to higher layers */
- struct l1ctl_info_dl *data;
- data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl));
- if (data == NULL)
- return -ENOMEM;
-
- /* Fill in some downlink info */
- data->chan_nr = trx_lchan_desc[lchan->type].chan_nr | ts->index;
- data->link_id = trx_lchan_desc[lchan->type].link_id;
- data->band_arfcn = htons(trx->band_arfcn);
- data->frame_nr = htonl(fn);
- data->rx_level = -(meas->rssi);
-
- /* FIXME: set proper values */
- data->num_biterr = 0;
- data->fire_crc = 0;
- data->snr = 0;
-
- l1ctl_tx_fbsb_conf(trx->l1l, 0, data, bsic);
-
- /* Update BSIC value of trx_instance */
- trx->bsic = bsic;
+ /* Update BSIC value in the scheduler state */
+ lchan->ts->sched->bsic = bsic;
- return 0;
+ return handle_sch_ind(lchan->ts->sched, time.fn, bsic);
}
diff --git a/src/host/trxcon/src/sched_lchan_tchf.c b/src/host/trxcon/src/sched_lchan_tchf.c
new file mode 100644
index 00000000..37e0cea3
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_tchf.c
@@ -0,0 +1,441 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2021-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
+ * 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Burst Payload LENgth (short alias) */
+#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD
+
+/* Burst BUFfer capacity (in BPLEN units) */
+#define BUFMAX 24
+
+/* Burst BUFfer position macros */
+#define BUFPOS(buf, n) &buf[(n) * BPLEN]
+#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8))
+
+/* 3GPP TS 45.009, table 3.2.1.3-{1,3}: AMR on Downlink TCH/F.
+ *
+ * +---+---+---+---+---+---+---+---+
+ * | a | b | c | d | e | f | g | h | Burst 'a' received first
+ * +---+---+---+---+---+---+---+---+
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Speech/FACCH frame (bursts 'a' .. 'h')
+ *
+ * TDMA frame number of burst 'h' is always used as the table index. */
+static const uint8_t sched_tchf_dl_amr_cmi_map[26] = {
+ [11] = 1, /* TCH/F: a=4 / h=11 */
+ [20] = 1, /* TCH/F: a=13 / h=20 */
+ [3] = 1, /* TCH/F: a=21 / h=3 (21+7=28, 25 is idle -> 29. 29%26=3) */
+};
+
+/* TDMA frame number of burst 'a' should be used as the table index. */
+static const uint8_t sched_tchf_ul_amr_cmi_map[26] = {
+ [0] = 1, /* TCH/F: a=0 */
+ [8] = 1, /* TCH/F: a=8 */
+ [17] = 1, /* TCH/F: a=17 */
+};
+
+static int decode_fr_facch(struct l1sched_lchan_state *lchan)
+{
+ uint8_t data[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total;
+ int rc;
+
+ rc = gsm0503_tch_fr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts),
+ &n_errors, &n_bits_total);
+ if (rc != GSM_MACBLOCK_LEN)
+ return rc;
+
+ /* calculate AVG of the measurements (FACCH/F takes 8 bursts) */
+ l1sched_lchan_meas_avg(lchan, 8);
+
+ l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+int rx_tchf_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ int n_errors = -1, n_bits_total = 0, rc;
+ sbit_t *bursts_p, *burst;
+ uint8_t tch_data[290];
+ size_t tch_data_len;
+ uint32_t *mask;
+ int amr = 0;
+ uint8_t ft;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Traffic received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ if (bi->bid == 0) {
+ /* Shift the burst buffer by 4 bursts leftwards */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN);
+ *mask = *mask << 4;
+ } else {
+ /* Align to the first burst of a block */
+ if (*mask == 0x00)
+ return 0;
+ }
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to end of buffer of 24 bursts */
+ burst = BUFPOS(bursts_p, 20 + bi->bid);
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until complete set of bursts */
+ if (bi->bid != 3)
+ return 0;
+
+ /* Calculate AVG of the measurements */
+ l1sched_lchan_meas_avg(lchan, 8); // XXX
+
+ /* Check for complete set of bursts */
+ if ((*mask & 0xff) != 0xff) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received incomplete (%s) traffic frame at fn=%u (%u/%u)\n",
+ l1sched_burst_mask2str(mask, 8), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % lchan->ts->mf_layout->period,
+ lchan->ts->mf_layout->period);
+ /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
+
+ }
+
+ /* TCH/F: speech and signalling frames are interleaved over 8 bursts, while
+ * CSD frames are interleaved over 22 bursts. Unless we're in CSD mode,
+ * decode only the last 8 bursts to avoid introducing additional delays. */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ 1, 0, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ 1, 1, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* we store tch_data + 2 header bytes, the amr variable set to
+ * 2 will allow us to skip the first 2 bytes in case we did
+ * receive an FACCH frame instead of a voice frame (we do not
+ * know this before we actually decode the frame) */
+ amr = 2;
+ rc = gsm0503_tch_afs_decode_dtx(&tch_data[amr], BUFTAIL8(bursts_p),
+ !sched_tchf_dl_amr_cmi_map[bi->fn % 26],
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ &lchan->amr.dl_ft,
+ &lchan->amr.dl_cmr,
+ &n_errors, &n_bits_total,
+ &lchan->amr.last_dtx);
+
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (lchan->amr.last_dtx == AMR_OTHER) {
+ ft = lchan->amr.codec[lchan->amr.dl_ft];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(&tch_data[0],
+ lchan->amr.codec[lchan->amr.dl_cmr],
+ ft, AMR_GOOD);
+ if (rc < 0)
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "osmo_amr_rtp_enc() returned rc=%d\n", rc);
+ }
+ break;
+ /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_14k5:
+ /* FACCH/F does not steal TCH/F14.4 frames, but only disturbs some bits */
+ decode_fr_facch(lchan);
+ rc = gsm0503_tch_fr144_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_12k0:
+ /* FACCH/F does not steal TCH/F9.6 frames, but only disturbs some bits */
+ decode_fr_facch(lchan);
+ rc = gsm0503_tch_fr96_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ /* FACCH/F does not steal TCH/F4.8 frames, but only disturbs some bits */
+ decode_fr_facch(lchan);
+ rc = gsm0503_tch_fr48_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ /* TCH/F2.4 employs the same interleaving as TCH/FS (8 bursts),
+ * so FACCH/F *does* steal TCH/F2.4 frames completely. */
+ if (decode_fr_facch(lchan) == GSM_MACBLOCK_LEN)
+ return 0; /* TODO: emit BFI? */
+ rc = gsm0503_tch_fr24_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ &n_errors, &n_bits_total);
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
+ return -EINVAL;
+ }
+
+ /* Check decoding result */
+ if (rc < 4) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+
+ /* Send BFI (DATA.ind without payload) */
+ tch_data_len = 0;
+ } else if (rc == GSM_MACBLOCK_LEN) {
+ /* FACCH received, forward it to the higher layers */
+ l1sched_lchan_emit_data_ind(lchan, &tch_data[amr], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ /* Send BFI (DATA.ind without payload) */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN)
+ return 0;
+ tch_data_len = 0;
+ } else {
+ /* A good TCH frame received */
+ tch_data_len = rc;
+ }
+
+ /* Send a traffic frame to the higher layers */
+ return l1sched_lchan_emit_data_ind(lchan, &tch_data[0], tch_data_len,
+ n_errors, n_bits_total, true);
+}
+
+int tx_tchf_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ struct msgb *msg_facch, *msg_tch, *msg;
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ /* Shift the burst buffer by 4 bursts leftwards for interleaving */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN);
+ *mask = *mask << 4;
+
+ /* dequeue a pair of TCH and FACCH frames */
+ msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
+ msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
+ /* prioritize FACCH over TCH */
+ msg = (msg_facch != NULL) ? msg_facch : msg_tch;
+
+ /* populate the buffer with bursts */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ if (msg == NULL)
+ msg = l1sched_lchan_prim_dummy_lapdm(lchan);
+ /* fall-through */
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ if (msg == NULL) {
+ /* transmit a dummy speech block with inverted CRC3 */
+ gsm0503_tch_fr_encode(bursts_p, NULL, 0, 1);
+ goto send_burst;
+ }
+ rc = gsm0503_tch_fr_encode(BUFPOS(bursts_p, 0),
+ msgb_l2(msg),
+ msgb_l2len(msg), 1);
+ break;
+ case GSM48_CMODE_SPEECH_AMR:
+ {
+ bool amr_fn_is_cmr = !sched_tchf_ul_amr_cmi_map[br->fn % 26];
+ const uint8_t *data = msg ? msgb_l2(msg) : NULL;
+ size_t data_len = msg ? msgb_l2len(msg) : 0;
+
+ if (msg == NULL) {
+ /* TODO: It's not clear what to do for TCH/AFS.
+ * TODO: Send dummy FACCH maybe? */
+ goto send_burst; /* send something */
+ }
+
+ if (data_len != GSM_MACBLOCK_LEN) { /* TCH/AFS: speech */
+ if (!l1sched_lchan_amr_prim_is_valid(lchan, msg, amr_fn_is_cmr))
+ goto free_bad_msg;
+ /* pull the AMR header - sizeof(struct amr_hdr) */
+ data_len -= 2;
+ data += 2;
+ }
+
+ rc = gsm0503_tch_afs_encode(BUFPOS(bursts_p, 0),
+ data, data_len,
+ amr_fn_is_cmr,
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
+ break;
+ }
+ /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_14k5:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 290);
+ gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[290];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_12k0:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 4 * 60);
+ gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[4 * 60];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 2 * 60);
+ gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[2 * 60];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ if ((msg = msg_facch) != NULL) {
+ /* FACCH/F does steal a TCH/F2.4 frame completely */
+ rc = gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ } else if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 2 * 36);
+ rc = gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ } else {
+ ubit_t idle[2 * 36];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ goto send_burst;
+ }
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "TCH mode %s is unknown or not supported\n",
+ gsm48_chan_mode_name(lchan->tch_mode));
+ goto free_bad_msg;
+ }
+
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+free_bad_msg:
+ msgb_free(msg_facch);
+ msgb_free(msg_tch);
+ return -EINVAL;
+ }
+
+ /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ msgb_free((msg == msg_facch) ? msg_tch : msg_facch);
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = BUFPOS(bursts_p, br->bid);
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid);
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_lchan_tchh.c b/src/host/trxcon/src/sched_lchan_tchh.c
new file mode 100644
index 00000000..99e26808
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_tchh.c
@@ -0,0 +1,622 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2018-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020-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
+ * 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Burst Payload LENgth (short alias) */
+#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD
+
+/* Burst BUFfer capacity (in BPLEN units) */
+#define BUFMAX 24
+
+/* Burst BUFfer position macros */
+#define BUFPOS(buf, n) &buf[(n) * BPLEN]
+#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8))
+
+/* 3GPP TS 45.009, table 3.2.1.3-{2,4}: AMR on Downlink TCH/H.
+ *
+ * +---+---+---+---+---+---+
+ * | a | b | c | d | e | f | Burst 'a' received first
+ * +---+---+---+---+---+---+
+ * ^^^^^^^^^^^^^^^^^^^^^^^ FACCH frame (bursts 'a' .. 'f')
+ * ^^^^^^^^^^^^^^^ Speech frame (bursts 'a' .. 'd')
+ *
+ * TDMA frame number of burst 'f' is always used as the table index. */
+static const uint8_t sched_tchh_dl_amr_cmi_map[26] = {
+ [15] = 1, /* TCH/H(0): a=4 / d=10 / f=15 */
+ [23] = 1, /* TCH/H(0): a=13 / d=19 / f=23 */
+ [6] = 1, /* TCH/H(0): a=21 / d=2 / f=6 */
+
+ [16] = 1, /* TCH/H(1): a=5 / d=11 / f=16 */
+ [24] = 1, /* TCH/H(1): a=14 / d=20 / f=24 */
+ [7] = 1, /* TCH/H(1): a=22 / d=3 / f=7 */
+};
+
+/* TDMA frame number of burst 'a' is always used as the table index. */
+static const uint8_t sched_tchh_ul_amr_cmi_map[26] = {
+ [0] = 1, /* TCH/H(0): a=0 */
+ [8] = 1, /* TCH/H(0): a=8 */
+ [17] = 1, /* TCH/H(0): a=17 */
+
+ [1] = 1, /* TCH/H(1): a=1 */
+ [9] = 1, /* TCH/H(1): a=9 */
+ [18] = 1, /* TCH/H(1): a=18 */
+};
+
+static const uint8_t tch_h0_traffic_block_map[3][4] = {
+ /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */
+ { 0, 2, 4, 6 },
+ { 4, 6, 8, 10 },
+ { 8, 10, 0, 2 },
+};
+
+static const uint8_t tch_h1_traffic_block_map[3][4] = {
+ /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */
+ { 1, 3, 5, 7 },
+ { 5, 7, 9, 11 },
+ { 9, 11, 1, 3 },
+};
+
+static const uint8_t tch_h0_dl_facch_block_map[3][6] = {
+ /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */
+ { 4, 6, 8, 10, 13, 15 },
+ { 13, 15, 17, 19, 21, 23 },
+ { 21, 23, 0, 2, 4, 6 },
+};
+
+static const uint8_t tch_h0_ul_facch_block_map[3][6] = {
+ /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */
+ { 0, 2, 4, 6, 8, 10 },
+ { 8, 10, 13, 15, 17, 19 },
+ { 17, 19, 21, 23, 0, 2 },
+};
+
+static const uint8_t tch_h1_dl_facch_block_map[3][6] = {
+ /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */
+ { 5, 7, 9, 11, 14, 16 },
+ { 14, 16, 18, 20, 22, 24 },
+ { 22, 24, 1, 3, 5, 7 },
+};
+
+const uint8_t tch_h1_ul_facch_block_map[3][6] = {
+ /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */
+ { 1, 3, 5, 7, 9, 11 },
+ { 9, 11, 14, 16, 18, 20 },
+ { 18, 20, 22, 24, 1, 3 },
+};
+
+/* FACCH/H channel mapping for Downlink (see 3GPP TS 45.002, table 1).
+ * This mapping is valid for both FACCH/H(0) and FACCH/H(1).
+ * TDMA frame number of burst 'f' is used as the table index. */
+static const uint8_t sched_tchh_dl_facch_map[26] = {
+ [15] = 1, /* FACCH/H(0): B0(4,6,8,10,13,15) */
+ [16] = 1, /* FACCH/H(1): B0(5,7,9,11,14,16) */
+ [23] = 1, /* FACCH/H(0): B1(13,15,17,19,21,23) */
+ [24] = 1, /* FACCH/H(1): B1(14,16,18,20,22,24) */
+ [6] = 1, /* FACCH/H(0): B2(21,23,0,2,4,6) */
+ [7] = 1, /* FACCH/H(1): B2(22,24,1,3,5,7) */
+};
+
+/* 3GPP TS 45.002, table 2 in clause 7: Mapping tables for TCH/H2.4 and TCH/H4.8.
+ *
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * TCH/H(0): B0(0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19)
+ * TCH/H(1): B0(1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20)
+ * TCH/H(0): B1(8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2)
+ * TCH/H(1): B1(9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3)
+ * TCH/H(0): B2(17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10)
+ * TCH/H(1): B2(18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11)
+ *
+ * TDMA frame number of burst 'a' % 26 is the table index.
+ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
+static const uint8_t sched_tchh_ul_csd_map[26] = {
+ [0] = 1, /* TCH/H(0): B0(0 ... 19) */
+ [1] = 1, /* TCH/H(1): B0(1 ... 20) */
+ [8] = 1, /* TCH/H(0): B1(8 ... 2) */
+ [9] = 1, /* TCH/H(1): B1(9 ... 3) */
+ [17] = 1, /* TCH/H(0): B2(17 ... 10) */
+ [18] = 1, /* TCH/H(1): B2(18 ... 11) */
+};
+
+/* TDMA frame number of burst 'v' % 26 is the table index.
+ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
+static const uint8_t sched_tchh_dl_csd_map[26] = {
+ [19] = 1, /* TCH/H(0): B0(0 ... 19) */
+ [20] = 1, /* TCH/H(1): B0(1 ... 20) */
+ [2] = 1, /* TCH/H(0): B1(8 ... 2) */
+ [3] = 1, /* TCH/H(1): B1(9 ... 3) */
+ [10] = 1, /* TCH/H(0): B2(17 ... 10) */
+ [11] = 1, /* TCH/H(1): B2(18 ... 11) */
+};
+
+/**
+ * Can a TCH/H block transmission be initiated / finished
+ * on a given frame number and a given channel type?
+ *
+ * See GSM 05.02, clause 7, table 1
+ *
+ * @param chan channel type (L1SCHED_TCHH_0 or L1SCHED_TCHH_1)
+ * @param fn the current frame number
+ * @param ul Uplink or Downlink?
+ * @param facch FACCH/H or traffic?
+ * @param start init or end of transmission?
+ * @return true (yes) or false (no)
+ */
+bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan,
+ uint32_t fn, bool ul, bool facch, bool start)
+{
+ uint8_t fn_mf;
+ int i = 0;
+
+ /* Just to be sure */
+ OSMO_ASSERT(chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1);
+
+ /* Calculate a modulo */
+ fn_mf = facch ? (fn % 26) : (fn % 13);
+
+#define MAP_GET_POS(map) \
+ (start ? 0 : ARRAY_SIZE(map[i]) - 1)
+
+#define BLOCK_MAP_FN(map) \
+ do { \
+ if (map[i][MAP_GET_POS(map)] == fn_mf) \
+ return true; \
+ } while (++i < ARRAY_SIZE(map))
+
+ /* Choose a proper block map */
+ if (facch) {
+ if (ul) {
+ if (chan == L1SCHED_TCHH_0)
+ BLOCK_MAP_FN(tch_h0_ul_facch_block_map);
+ else
+ BLOCK_MAP_FN(tch_h1_ul_facch_block_map);
+ } else {
+ if (chan == L1SCHED_TCHH_0)
+ BLOCK_MAP_FN(tch_h0_dl_facch_block_map);
+ else
+ BLOCK_MAP_FN(tch_h1_dl_facch_block_map);
+ }
+ } else {
+ if (chan == L1SCHED_TCHH_0)
+ BLOCK_MAP_FN(tch_h0_traffic_block_map);
+ else
+ BLOCK_MAP_FN(tch_h1_traffic_block_map);
+ }
+
+ return false;
+}
+
+static int decode_hr_facch(struct l1sched_lchan_state *lchan)
+{
+ uint8_t data[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total;
+ int rc;
+
+ rc = gsm0503_tch_hr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts),
+ &n_errors, &n_bits_total);
+ if (rc != GSM_MACBLOCK_LEN)
+ return rc;
+
+ /* calculate AVG of the measurements (FACCH/H takes 6 bursts) */
+ l1sched_lchan_meas_avg(lchan, 6);
+
+ l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+int rx_tchh_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ int n_errors = -1, n_bits_total = 0, rc;
+ sbit_t *bursts_p, *burst;
+ uint8_t tch_data[240];
+ size_t tch_data_len;
+ uint32_t *mask;
+ int amr = 0;
+ uint8_t ft;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Traffic received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ if (bi->bid == 0) {
+ /* Shift the burst buffer by 2 bursts leftwards */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
+ *mask = *mask << 2;
+ }
+
+ if (*mask == 0x00) {
+ /* Align to the first burst */
+ if (bi->bid > 0)
+ return 0;
+
+ /* Align reception of the first FACCH/H frame */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN) {
+ if (!l1sched_tchh_facch_start(lchan->type, bi->fn, 0))
+ return 0;
+ }
+ }
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to the end of buffer of 24 bursts */
+ burst = BUFPOS(bursts_p, 20 + bi->bid);
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until the second burst */
+ if (bi->bid != 1)
+ return 0;
+
+ /* Wait for complete set of bursts */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ /* FACCH/H is interleaved over 6 bursts */
+ if ((*mask & 0x3f) != 0x3f)
+ return 0;
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ /* Data (CSD) is interleaved over 22 bursts */
+ if ((*mask & 0x3fffff) != 0x3fffff)
+ return 0;
+ if (!sched_tchh_dl_csd_map[bi->fn % 26])
+ return 0; /* CSD: skip decoding attempt, need 2 more bursts */
+ break;
+ default:
+ /* Speech is interleaved over 4 bursts */
+ if ((*mask & 0x0f) != 0x0f)
+ return 0;
+ break;
+ }
+
+ /* Skip decoding attempt in case of FACCH/H */
+ if (lchan->dl_ongoing_facch) {
+ /* Send BFI (DATA.ind without payload) for the 2nd stolen TCH frame */
+ l1sched_lchan_meas_avg(lchan, 4);
+ l1sched_lchan_emit_data_ind(lchan, NULL, 0, 0, 0, true);
+ lchan->dl_ongoing_facch = false;
+ return 0;
+ }
+
+ /* TCH/H: speech and signalling frames are interleaved over 4 and 6 bursts,
+ * respectively, while CSD frames are interleaved over 22 bursts. Unless
+ * we're in CSD mode, decode only the last 6 bursts to avoid introducing
+ * additional delays. */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ case GSM48_CMODE_SPEECH_V1: /* HR */
+ rc = gsm0503_tch_hr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ !sched_tchh_dl_facch_map[bi->fn % 26],
+ &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* See comment in function rx_tchf_fn() */
+ amr = 2;
+ rc = gsm0503_tch_ahs_decode_dtx(&tch_data[amr], BUFTAIL8(bursts_p),
+ !sched_tchh_dl_facch_map[bi->fn % 26],
+ !sched_tchh_dl_amr_cmi_map[bi->fn % 26],
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ &lchan->amr.dl_ft,
+ &lchan->amr.dl_cmr,
+ &n_errors, &n_bits_total,
+ &lchan->amr.last_dtx);
+
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (lchan->amr.last_dtx == AMR_OTHER) {
+ ft = lchan->amr.codec[lchan->amr.dl_ft];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(&tch_data[0],
+ lchan->amr.codec[lchan->amr.dl_cmr],
+ ft, AMR_GOOD);
+ if (rc < 0)
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "osmo_amr_rtp_enc() returned rc=%d\n", rc);
+ }
+ break;
+ /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ /* FACCH/H does not steal TCH/H4.8 frames, but only disturbs some bits */
+ decode_hr_facch(lchan);
+ rc = gsm0503_tch_hr48_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ /* FACCH/H does not steal TCH/H2.4 frames, but only disturbs some bits */
+ decode_hr_facch(lchan);
+ rc = gsm0503_tch_hr24_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
+ return -EINVAL;
+ }
+
+ /* Check decoding result */
+ if (rc < 4) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+
+ /* Send BFI (DATA.ind without payload) */
+ tch_data_len = 0;
+ } else if (rc == GSM_MACBLOCK_LEN) {
+ /* Skip decoding of the next 2 stolen bursts */
+ lchan->dl_ongoing_facch = true;
+
+ /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */
+ l1sched_lchan_meas_avg(lchan, 6);
+
+ /* FACCH/H received, forward to the higher layers */
+ l1sched_lchan_emit_data_ind(lchan, &tch_data[amr], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ /* Send BFI (DATA.ind without payload) for the 1st stolen TCH frame */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN)
+ return 0;
+ tch_data_len = 0;
+ } else {
+ /* A good TCH frame received */
+ tch_data_len = rc;
+ }
+
+ /* Calculate AVG of the measurements (traffic takes 4 bursts) */
+ l1sched_lchan_meas_avg(lchan, 4);
+
+ /* Send a traffic frame to the higher layers */
+ return l1sched_lchan_emit_data_ind(lchan, &tch_data[0], tch_data_len,
+ n_errors, n_bits_total, true);
+}
+
+int tx_tchh_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ struct msgb *msg_facch, *msg_tch, *msg;
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ if (*mask == 0x00) {
+ /* Align transmission of the first frame */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1))
+ return 0;
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ if (!sched_tchh_ul_csd_map[br->fn % 26])
+ return 0;
+ break;
+ }
+ }
+
+ /* Shift the burst buffer by 2 bursts leftwards for interleaving */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
+ *mask = *mask << 2;
+
+ /* If FACCH/H blocks are still pending */
+ if (lchan->ul_facch_blocks > 2) {
+ struct msgb *msg = l1sched_lchan_prim_dequeue_tch(lchan, false);
+ msgb_free(msg); /* drop 2nd TCH/HS block */
+ goto send_burst;
+ }
+
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ /* CSD: skip dequeueing/encoding, send 2 more bursts */
+ if (!sched_tchh_ul_csd_map[br->fn % 26])
+ goto send_burst;
+ break;
+ }
+
+ /* dequeue a pair of TCH and FACCH frames */
+ msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
+ if (l1sched_tchh_facch_start(lchan->type, br->fn, true))
+ msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
+ else
+ msg_facch = NULL;
+ /* prioritize FACCH over TCH */
+ msg = (msg_facch != NULL) ? msg_facch : msg_tch;
+
+ /* populate the buffer with bursts */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1))
+ goto send_burst; /* XXX: should not happen */
+ if (msg == NULL)
+ msg = l1sched_lchan_prim_dummy_lapdm(lchan);
+ /* fall-through */
+ case GSM48_CMODE_SPEECH_V1:
+ if (msg == NULL) {
+ /* transmit a dummy speech block with inverted CRC3 */
+ gsm0503_tch_hr_encode(bursts_p, NULL, 0);
+ goto send_burst;
+ }
+ rc = gsm0503_tch_hr_encode(BUFPOS(bursts_p, 0),
+ msgb_l2(msg),
+ msgb_l2len(msg));
+ break;
+ case GSM48_CMODE_SPEECH_AMR:
+ {
+ bool amr_fn_is_cmr = !sched_tchh_ul_amr_cmi_map[br->fn % 26];
+ const uint8_t *data = msg ? msgb_l2(msg) : NULL;
+ size_t data_len = msg ? msgb_l2len(msg) : 0;
+
+ if (msg == NULL) {
+ /* TODO: It's not clear what to do for TCH/AHS.
+ * TODO: Send dummy FACCH maybe? */
+ goto send_burst; /* send garbage */
+ }
+
+ if (data_len != GSM_MACBLOCK_LEN) { /* TCH/AHS: speech */
+ if (!l1sched_lchan_amr_prim_is_valid(lchan, msg, amr_fn_is_cmr))
+ goto free_bad_msg;
+ /* pull the AMR header - sizeof(struct amr_hdr) */
+ data_len -= 2;
+ data += 2;
+ }
+
+ rc = gsm0503_tch_ahs_encode(BUFPOS(bursts_p, 0),
+ data, data_len,
+ amr_fn_is_cmr,
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
+ break;
+ }
+ /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 4 * 60);
+ gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[4 * 60];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 4 * 36);
+ gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[4 * 36];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "TCH mode %s is unknown or not supported\n",
+ gsm48_chan_mode_name(lchan->tch_mode));
+ goto free_bad_msg;
+ }
+
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+free_bad_msg:
+ msgb_free(msg_facch);
+ msgb_free(msg_tch);
+ return -EINVAL;
+ }
+
+ if (msgb_l2len(msg) == GSM_MACBLOCK_LEN)
+ lchan->ul_facch_blocks = 6;
+
+ /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ msgb_free((msg == msg_facch) ? msg_tch : msg_facch);
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = BUFPOS(bursts_p, br->bid);
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid);
+
+ /* In case of a FACCH/H frame, one block less */
+ if (lchan->ul_facch_blocks)
+ lchan->ul_facch_blocks--;
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_lchan_xcch.c b/src/host/trxcon/src/sched_lchan_xcch.c
new file mode 100644
index 00000000..52b5d1e6
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_xcch.c
@@ -0,0 +1,181 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+int rx_data_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ uint8_t l2[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total, rc;
+ sbit_t *bursts_p, *burst;
+ uint32_t *mask;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Data received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bi->bid != 0)
+ return 0;
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to buffer of 4 bursts */
+ burst = bursts_p + bi->bid * 116;
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until complete set of bursts */
+ if (bi->bid != 3)
+ return 0;
+
+ /* Calculate AVG of the measurements */
+ l1sched_lchan_meas_avg(lchan, 4);
+
+ /* Check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received incomplete (%s) data frame at fn=%u (%u/%u)\n",
+ l1sched_burst_mask2str(mask, 4), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % lchan->ts->mf_layout->period,
+ lchan->ts->mf_layout->period);
+ /* NOTE: xCCH has an insane amount of redundancy for error
+ * correction, so even just 2 valid bursts might be enough
+ * to reconstruct some L2 frames. This is why we do not
+ * abort here. */
+ }
+
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
+ /* Attempt to decode */
+ rc = gsm0503_xcch_decode(l2, bursts_p, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+ }
+
+ /* Send a L2 frame to the higher layers */
+ return l1sched_lchan_emit_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+}
+
+static struct msgb *prim_dequeue_xcch(struct l1sched_lchan_state *lchan)
+{
+ struct msgb *msg;
+
+ if (L1SCHED_CHAN_IS_SACCH(lchan->type))
+ return l1sched_lchan_prim_dequeue_sacch(lchan);
+ if ((msg = msgb_dequeue(&lchan->tx_prims)) == NULL)
+ return NULL;
+
+ /* Check the prim payload length */
+ if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Primitive has odd length %u (expected %u), so dropping...\n",
+ msgb_l2len(msg), GSM_MACBLOCK_LEN);
+ msgb_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+int tx_data_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ *mask = *mask << 4;
+
+ struct msgb *msg = prim_dequeue_xcch(lchan);
+ if (msg == NULL)
+ msg = l1sched_lchan_prim_dummy_lapdm(lchan);
+ OSMO_ASSERT(msg != NULL);
+
+ /* Encode payload */
+ rc = gsm0503_xcch_encode(bursts_p, msgb_l2(msg));
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = bursts_p + br->bid * 116;
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid);
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_mframe.c b/src/host/trxcon/src/sched_mframe.c
new file mode 100644
index 00000000..0d95e0ac
--- /dev/null
+++ b/src/host/trxcon/src/sched_mframe.c
@@ -0,0 +1,2102 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: channel combinations, burst mapping
+ *
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2015 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 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/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Non-combined CCCH */
+static const struct l1sched_tdma_frame frame_bcch[51] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_RACH, 0 },
+};
+
+/* Combined CCCH+SDCCH4 */
+static const struct l1sched_tdma_frame frame_bcch_sdcch4[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_2, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_2, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_SACCH4_2, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_SACCH4_2, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_3, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_3, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_3, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_SACCH4_0, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_SACCH4_0, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_SACCH4_0, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_SACCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 1, L1SCHED_SDCCH4_2, 0 },
+ { L1SCHED_SACCH4_1, 2, L1SCHED_SDCCH4_2, 1 },
+ { L1SCHED_SACCH4_1, 3, L1SCHED_SDCCH4_2, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_2, 3 },
+
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_0, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_0, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_SACCH4_0, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_SACCH4_0, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_1, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_1, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_1, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_1, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_SACCH4_2, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_SACCH4_2, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_SACCH4_2, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_SACCH4_2, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 1, L1SCHED_SDCCH4_2, 0 },
+ { L1SCHED_SACCH4_3, 2, L1SCHED_SDCCH4_2, 1 },
+ { L1SCHED_SACCH4_3, 3, L1SCHED_SDCCH4_2, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_2, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_bcch_sdcch4_cbch[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_IDLE, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_IDLE, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_IDLE, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_3, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_3, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_3, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_SACCH4_0, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_SACCH4_0, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_SACCH4_0, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_SACCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SACCH4_1, 2, L1SCHED_IDLE, 1 },
+ { L1SCHED_SACCH4_1, 3, L1SCHED_IDLE, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 3 },
+
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_0, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_0, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_SACCH4_0, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_SACCH4_0, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_1, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_1, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_1, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_1, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_IDLE, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_IDLE, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_IDLE, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SACCH4_3, 2, L1SCHED_IDLE, 1 },
+ { L1SCHED_SACCH4_3, 3, L1SCHED_IDLE, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_sdcch8[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_5, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_5, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_5, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_5, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_6, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_6, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_6, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_6, 3 },
+ { L1SCHED_SDCCH8_2, 0, L1SCHED_SACCH8_7, 0 },
+ { L1SCHED_SDCCH8_2, 1, L1SCHED_SACCH8_7, 1 },
+ { L1SCHED_SDCCH8_2, 2, L1SCHED_SACCH8_7, 2 },
+ { L1SCHED_SDCCH8_2, 3, L1SCHED_SACCH8_7, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_SDCCH8_2, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_SDCCH8_2, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_SDCCH8_2, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_SDCCH8_2, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_0, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_0, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_0, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_0, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_1, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_1, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_1, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_1, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_SACCH8_2, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_SACCH8_2, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_SACCH8_2, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_SACCH8_2, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_3, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_3, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_3, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_3, 3, L1SCHED_SACCH8_0, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 3 },
+
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_1, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_1, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_1, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_1, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_2, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_2, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_2, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_2, 3 },
+ { L1SCHED_SDCCH8_2, 0, L1SCHED_SACCH8_3, 0 },
+ { L1SCHED_SDCCH8_2, 1, L1SCHED_SACCH8_3, 1 },
+ { L1SCHED_SDCCH8_2, 2, L1SCHED_SACCH8_3, 2 },
+ { L1SCHED_SDCCH8_2, 3, L1SCHED_SACCH8_3, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_SDCCH8_2, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_SDCCH8_2, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_SDCCH8_2, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_SDCCH8_2, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_4, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_4, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_4, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_4, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_5, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_5, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_5, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_5, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_SACCH8_6, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_SACCH8_6, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_SACCH8_6, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_SACCH8_6, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_7, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_7, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_7, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_7, 3, L1SCHED_SACCH8_4, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_sdcch8_cbch[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_5, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_5, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_5, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_5, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_6, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_6, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_6, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_6, 3 },
+ { L1SCHED_SDCCH8_CBCH, 0, L1SCHED_SACCH8_7, 0 },
+ { L1SCHED_SDCCH8_CBCH, 1, L1SCHED_SACCH8_7, 1 },
+ { L1SCHED_SDCCH8_CBCH, 2, L1SCHED_SACCH8_7, 2 },
+ { L1SCHED_SDCCH8_CBCH, 3, L1SCHED_SACCH8_7, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_IDLE, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_IDLE, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_IDLE, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_0, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_0, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_0, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_0, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_1, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_1, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_1, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_1, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_IDLE, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_IDLE, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_IDLE, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_3, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_3, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_3, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_3, 3, L1SCHED_SACCH8_0, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 3 },
+
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_1, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_1, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_1, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_1, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_IDLE, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_IDLE, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_IDLE, 3 },
+ { L1SCHED_SDCCH8_CBCH, 0, L1SCHED_SACCH8_3, 0 },
+ { L1SCHED_SDCCH8_CBCH, 1, L1SCHED_SACCH8_3, 1 },
+ { L1SCHED_SDCCH8_CBCH, 2, L1SCHED_SACCH8_3, 2 },
+ { L1SCHED_SDCCH8_CBCH, 3, L1SCHED_SACCH8_3, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_IDLE, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_IDLE, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_IDLE, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_4, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_4, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_4, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_4, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_5, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_5, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_5, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_5, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_SACCH8_6, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_SACCH8_6, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_SACCH8_6, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_SACCH8_6, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_7, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_7, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_7, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_7, 3, L1SCHED_SACCH8_4, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts0[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts1[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts2[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts3[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts4[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts5[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts6[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts7[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts01[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts23[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts45[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts67[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_pdch[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 0, L1SCHED_PTCCH, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 1, L1SCHED_PTCCH, 1 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 2, L1SCHED_PTCCH, 2 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 3, L1SCHED_PTCCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+/* Logical channel mask for a single channel */
+#define M64(x) \
+ ((uint64_t) 0x01 << x)
+
+/* Logical channel mask for BCCH+CCCH */
+#define M64_BCCH_CCCH \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_FCCH) \
+ | M64(L1SCHED_SCH) \
+ | M64(L1SCHED_BCCH) \
+ | M64(L1SCHED_RACH) \
+ | M64(L1SCHED_CCCH)
+
+/* Logical channel mask for SDCCH4 (with SACCH, all sub-channels) */
+#define M64_SDCCH4 \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_SDCCH4_0) | M64(L1SCHED_SACCH4_0) \
+ | M64(L1SCHED_SDCCH4_1) | M64(L1SCHED_SACCH4_1) \
+ | M64(L1SCHED_SDCCH4_2) | M64(L1SCHED_SACCH4_2) \
+ | M64(L1SCHED_SDCCH4_3) | M64(L1SCHED_SACCH4_3)
+
+/* Logical channel mask for SDCCH8 (with SACCH, all sub-channels) */
+#define M64_SDCCH8 \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_SDCCH8_0) | M64(L1SCHED_SACCH8_0) \
+ | M64(L1SCHED_SDCCH8_1) | M64(L1SCHED_SACCH8_1) \
+ | M64(L1SCHED_SDCCH8_2) | M64(L1SCHED_SACCH8_2) \
+ | M64(L1SCHED_SDCCH8_3) | M64(L1SCHED_SACCH8_3) \
+ | M64(L1SCHED_SDCCH8_4) | M64(L1SCHED_SACCH8_4) \
+ | M64(L1SCHED_SDCCH8_5) | M64(L1SCHED_SACCH8_5) \
+ | M64(L1SCHED_SDCCH8_6) | M64(L1SCHED_SACCH8_6) \
+ | M64(L1SCHED_SDCCH8_7) | M64(L1SCHED_SACCH8_7)
+
+/* Logical channel mask for TCH/F (with SACCH) */
+#define M64_TCHF \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_TCHF) | M64(L1SCHED_SACCHTF)
+
+/* Logical channel mask for TCH/H (with SACCH, all sub-channels) */
+#define M64_TCHH \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_TCHH_0) | M64(L1SCHED_SACCHTH_0) \
+ | M64(L1SCHED_TCHH_1) | M64(L1SCHED_SACCHTH_1)
+
+/**
+ * A few notes about frame count:
+ *
+ * 26 frame multiframe - traffic multiframe
+ * 51 frame multiframe - control multiframe
+ *
+ * 102 = 2 x 51 frame multiframe
+ * 104 = 4 x 26 frame multiframe
+ */
+static const struct l1sched_tdma_multiframe layouts[] = {
+ {
+ GSM_PCHAN_NONE, "NONE",
+ 0, 0xff,
+ 0x00,
+ NULL
+ },
+ {
+ GSM_PCHAN_CCCH, "BCCH+CCCH",
+ 51, 0xff,
+ M64_BCCH_CCCH,
+ frame_bcch
+ },
+ {
+ GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4",
+ 102, 0xff,
+ M64_BCCH_CCCH | M64_SDCCH4,
+ frame_bcch_sdcch4
+ },
+ {
+ GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH",
+ 102, 0xff,
+ M64_BCCH_CCCH | M64_SDCCH4 | M64(L1SCHED_SDCCH4_CBCH),
+ frame_bcch_sdcch4_cbch
+ },
+ {
+ GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8",
+ 102, 0xff,
+ M64_SDCCH8,
+ frame_sdcch8
+ },
+ {
+ GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH",
+ 102, 0xff,
+ M64_SDCCH8 | M64(L1SCHED_SDCCH8_CBCH),
+ frame_sdcch8_cbch
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x01,
+ M64_TCHF,
+ frame_tchf_ts0
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x02,
+ M64_TCHF,
+ frame_tchf_ts1
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x04,
+ M64_TCHF,
+ frame_tchf_ts2
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x08,
+ M64_TCHF,
+ frame_tchf_ts3
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x10,
+ M64_TCHF,
+ frame_tchf_ts4
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x20,
+ M64_TCHF,
+ frame_tchf_ts5
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x40,
+ M64_TCHF,
+ frame_tchf_ts6
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x80,
+ M64_TCHF,
+ frame_tchf_ts7
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0x03,
+ M64_TCHH,
+ frame_tchh_ts01
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0x0c,
+ M64_TCHH,
+ frame_tchh_ts23
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0x30,
+ M64_TCHH,
+ frame_tchh_ts45
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0xc0,
+ M64_TCHH,
+ frame_tchh_ts67
+ },
+ {
+ GSM_PCHAN_PDCH, "PDCH",
+ 104, 0xff,
+ M64(L1SCHED_PDTCH) | M64(L1SCHED_PTCCH),
+ frame_pdch
+ },
+};
+
+const struct l1sched_tdma_multiframe *
+l1sched_mframe_layout(enum gsm_phys_chan_config config, uint8_t tn)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(layouts); i++) {
+ if (layouts[i].chan_config != config)
+ continue;
+ if (~layouts[i].slotmask & (1 << tn))
+ continue;
+ return &layouts[i];
+ }
+
+ return NULL;
+}
diff --git a/src/host/trxcon/src/sched_prim.c b/src/host/trxcon/src/sched_prim.c
new file mode 100644
index 00000000..67be75e5
--- /dev/null
+++ b/src/host/trxcon/src/sched_prim.c
@@ -0,0 +1,410 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: primitive management
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (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
+ * 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.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+#define L1SCHED_PRIM_HEADROOM 64
+#define L1SCHED_PRIM_TAILROOM 512
+
+osmo_static_assert(sizeof(struct l1sched_prim) <= L1SCHED_PRIM_HEADROOM, l1sched_prim_size);
+
+const struct value_string l1sched_prim_type_names[] = {
+ { L1SCHED_PRIM_T_DATA, "DATA" },
+ { L1SCHED_PRIM_T_RACH, "RACH" },
+ { L1SCHED_PRIM_T_SCH, "SCH" },
+ { L1SCHED_PRIM_T_PCHAN_COMB, "PCHAN_COMB" },
+ { 0, NULL },
+};
+
+void l1sched_prim_init(struct msgb *msg,
+ enum l1sched_prim_type type,
+ enum osmo_prim_operation op)
+{
+ struct l1sched_prim *prim;
+
+ msg->l2h = msg->data;
+ msg->l1h = msgb_push(msg, sizeof(*prim));
+
+ prim = l1sched_prim_from_msgb(msg);
+ osmo_prim_init(&prim->oph, 0, type, op, msg);
+}
+
+struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
+ enum osmo_prim_operation op)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(L1SCHED_PRIM_HEADROOM + L1SCHED_PRIM_TAILROOM,
+ L1SCHED_PRIM_HEADROOM, "l1sched_prim");
+ if (msg == NULL)
+ return NULL;
+
+ l1sched_prim_init(msg, type, op);
+
+ return msg;
+}
+
+/**
+ * Composes a new primitive from cached RR Measurement Report.
+ *
+ * @param lchan lchan to assign a primitive
+ * @return SACCH primitive to be transmitted
+ */
+static struct msgb *prim_compose_mr(struct l1sched_lchan_state *lchan)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+ bool cached;
+
+ /* Allocate a new primitive */
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr | lchan->ts->index,
+ .link_id = L1SCHED_CH_LID_SACCH,
+ };
+
+ /* Check if the MR cache is populated (verify LAPDm header) */
+ cached = (lchan->sacch.mr_cache[2] != 0x00
+ && lchan->sacch.mr_cache[3] != 0x00
+ && lchan->sacch.mr_cache[4] != 0x00);
+ if (!cached) {
+ memcpy(&lchan->sacch.mr_cache[0],
+ &lchan->ts->sched->sacch_cache[0],
+ sizeof(lchan->sacch.mr_cache));
+ }
+
+ /* Compose a new Measurement Report primitive */
+ memcpy(msgb_put(msg, GSM_MACBLOCK_LEN),
+ &lchan->sacch.mr_cache[0],
+ GSM_MACBLOCK_LEN);
+
+ /* Inform about the cache usage count */
+ if (++lchan->sacch.mr_cache_usage > 5) {
+ LOGP_LCHAND(lchan, LOGL_NOTICE,
+ "SACCH MR cache usage count=%u > 5 "
+ "=> ancient measurements, please fix!\n",
+ lchan->sacch.mr_cache_usage);
+ }
+
+ LOGP_LCHAND(lchan, LOGL_NOTICE, "Using cached Measurement Report\n");
+
+ return msg;
+}
+
+/**
+ * Dequeues a SACCH primitive from transmit queue, if present.
+ * Otherwise dequeues a cached Measurement Report (the last
+ * received one). Finally, if the cache is empty, a "dummy"
+ * measurement report is used.
+ *
+ * According to 3GPP TS 04.08, section 3.4.1, SACCH channel
+ * accompanies either a traffic or a signaling channel. It
+ * has the particularity that continuous transmission must
+ * occur in both directions, so on the Uplink direction
+ * measurement result messages are sent at each possible
+ * occasion when nothing else has to be sent. The LAPDm
+ * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not
+ * applicable on SACCH channels!
+ *
+ * Unfortunately, 3GPP TS 04.08 doesn't clearly state
+ * which "else messages" besides Measurement Reports
+ * can be send by the MS on SACCH channels. However,
+ * in sub-clause 3.4.1 it's stated that the interval
+ * between two successive measurement result messages
+ * shall not exceed one L2 frame.
+ *
+ * @param lchan lchan to assign a primitive
+ * @return SACCH primitive to be transmitted
+ */
+struct msgb *l1sched_lchan_prim_dequeue_sacch(struct l1sched_lchan_state *lchan)
+{
+ struct msgb *msg_nmr = NULL;
+ struct msgb *msg_mr = NULL;
+ struct msgb *msg;
+ bool mr_now;
+
+ /* Shall we transmit MR now? */
+ mr_now = !lchan->sacch.mr_tx_last;
+
+#define PRIM_MSGB_IS_MR(msg) \
+ (l1sched_prim_data_from_msgb(msg)[5] == GSM48_PDISC_RR && \
+ l1sched_prim_data_from_msgb(msg)[6] == GSM48_MT_RR_MEAS_REP)
+
+ /* Iterate over all primitives in the queue */
+ llist_for_each_entry(msg, &lchan->tx_prims, list) {
+ /* Look for a Measurement Report */
+ if (!msg_mr && PRIM_MSGB_IS_MR(msg))
+ msg_mr = msg;
+
+ /* Look for anything else */
+ if (!msg_nmr && !PRIM_MSGB_IS_MR(msg))
+ msg_nmr = msg;
+
+ /* Should we look further? */
+ if (mr_now && msg_mr)
+ break; /* MR was found */
+ else if (!mr_now && msg_nmr)
+ break; /* something else was found */
+ }
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "SACCH MR selection: mr_tx_last=%d msg_mr=%p msg_nmr=%p\n",
+ lchan->sacch.mr_tx_last, msg_mr, msg_nmr);
+
+ /* Prioritize non-MR prim if possible */
+ if (mr_now && msg_mr)
+ msg = msg_mr;
+ else if (!mr_now && msg_nmr)
+ msg = msg_nmr;
+ else if (!mr_now && msg_mr)
+ msg = msg_mr;
+ else /* Nothing was found */
+ msg = NULL;
+
+ /* Have we found what we were looking for? */
+ if (msg) /* Dequeue if so */
+ llist_del(&msg->list);
+ else /* Otherwise compose a new MR */
+ msg = prim_compose_mr(lchan);
+
+ /* Update the cached report */
+ if (msg == msg_mr) {
+ memcpy(lchan->sacch.mr_cache, msgb_l2(msg), GSM_MACBLOCK_LEN);
+ lchan->sacch.mr_cache_usage = 0;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH MR cache has been updated\n");
+ }
+
+ /* Update the MR transmission state */
+ lchan->sacch.mr_tx_last = PRIM_MSGB_IS_MR(msg);
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH decision: %s\n",
+ PRIM_MSGB_IS_MR(msg) ? "Measurement Report" : "data frame");
+
+ return msg;
+}
+
+/**
+ * Dequeues either a FACCH, or a speech TCH primitive
+ * of a given channel type (Lm or Bm).
+ *
+ * @param lchan logical channel state
+ * @param facch FACCH (true) or speech (false) prim?
+ * @return either a FACCH, or a TCH primitive if found,
+ * otherwise NULL
+ */
+struct msgb *l1sched_lchan_prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch)
+{
+ struct msgb *msg;
+
+ /**
+ * There is no need to use the 'safe' list iteration here
+ * as an item removal is immediately followed by return.
+ */
+ llist_for_each_entry(msg, &lchan->tx_prims, list) {
+ bool is_facch = msgb_l2len(msg) == GSM_MACBLOCK_LEN;
+ if (is_facch != facch)
+ continue;
+
+ llist_del(&msg->list);
+ return msg;
+ }
+
+ return NULL;
+}
+
+/**
+ * Allocate a DATA.req with dummy LAPDm func=UI frame for the given logical channel.
+ * To be used when no suitable DATA.req is present in the Tx queue.
+ *
+ * @param lchan lchan to allocate a dummy primitive for
+ * @return an msgb with DATA.req primitive, or NULL
+ */
+struct msgb *l1sched_lchan_prim_dummy_lapdm(const struct l1sched_lchan_state *lchan)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+ uint8_t *ptr;
+
+ /* LAPDm func=UI is not applicable for SACCH */
+ OSMO_ASSERT(!L1SCHED_CHAN_IS_SACCH(lchan->type));
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr | lchan->ts->index,
+ .link_id = l1sched_lchan_desc[lchan->type].link_id,
+ };
+
+ ptr = msgb_put(msg, GSM_MACBLOCK_LEN);
+
+ /**
+ * TS 144.006, section 8.4.2.3 "Fill frames"
+ * A fill frame is a UI command frame for SAPI 0, P=0
+ * and with an information field of 0 octet length.
+ */
+ *(ptr++) = 0x01;
+ *(ptr++) = 0x03;
+ *(ptr++) = 0x01;
+
+ /**
+ * TS 144.006, section 5.2 "Frame delimitation and fill bits"
+ * Except for the first octet containing fill bits which shall
+ * be set to the binary value "00101011", each fill bit should
+ * be set to a random value when sent by the network.
+ */
+ *(ptr++) = 0x2b;
+ while (ptr < msg->tail)
+ *(ptr++) = (uint8_t)rand();
+
+ return msg;
+}
+
+int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
+ const uint8_t *data, size_t data_len,
+ int n_errors, int n_bits_total,
+ bool traffic)
+{
+ const struct l1sched_meas_set *meas = &lchan->meas_avg;
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_ind = (struct l1sched_prim_data_ind) {
+ .chdr = {
+ .frame_nr = meas->fn,
+ .chan_nr = lchan_desc->chan_nr | lchan->ts->index,
+ .link_id = lchan_desc->link_id,
+ .traffic = traffic,
+ },
+ .toa256 = meas->toa256,
+ .rssi = meas->rssi,
+ .n_errors = n_errors,
+ .n_bits_total = n_bits_total,
+ };
+
+ if (data_len > 0)
+ memcpy(msgb_put(msg, data_len), data, data_len);
+
+ return l1sched_prim_to_user(lchan->ts->sched, msg);
+}
+
+int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, uint32_t fn)
+{
+ struct l1sched_prim *prim;
+
+ OSMO_ASSERT(msg != NULL);
+
+ /* convert from DATA.req to DATA.cnf */
+ prim = l1sched_prim_from_msgb(msg);
+ prim->oph.operation = PRIM_OP_CONFIRM;
+
+ switch (prim->oph.primitive) {
+ case L1SCHED_PRIM_T_DATA:
+ prim->data_cnf.frame_nr = fn;
+ break;
+ case L1SCHED_PRIM_T_RACH:
+ prim->rach_cnf.chdr.frame_nr = fn;
+ break;
+ default:
+ /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ return l1sched_prim_to_user(lchan->ts->sched, msg);
+}
+
+static int prim_enqeue(struct l1sched_state *sched, struct msgb *msg,
+ const struct l1sched_prim_chdr *chdr)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct l1sched_lchan_state *lchan;
+
+ lchan = l1sched_find_lchan_by_chan_nr(sched, chdr->chan_nr, chdr->link_id);
+ if (OSMO_UNLIKELY(lchan == NULL || !lchan->active)) {
+ LOGP_SCHEDD(sched, LOGL_ERROR,
+ "No [active] lchan for primitive " L1SCHED_PRIM_STR_FMT " "
+ "(fn=%u, chan_nr=0x%02x, link_id=0x%02x, len=%u): %s\n",
+ L1SCHED_PRIM_STR_ARGS(prim),
+ chdr->frame_nr, chdr->chan_nr, chdr->link_id,
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -ENODEV;
+ }
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Enqueue primitive " L1SCHED_PRIM_STR_FMT " "
+ "(fn=%u, chan_nr=0x%02x, link_id=0x%02x, len=%u): %s\n",
+ L1SCHED_PRIM_STR_ARGS(prim),
+ chdr->frame_nr, chdr->chan_nr, chdr->link_id,
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+
+ msgb_enqueue(&lchan->tx_prims, msg);
+ return 0;
+}
+
+int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+
+ LOGP_SCHEDD(sched, LOGL_DEBUG,
+ "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST):
+ return prim_enqeue(sched, msg, &prim->data_req);
+ case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST):
+ return prim_enqeue(sched, msg, &prim->rach_req.chdr);
+ default:
+ LOGP_SCHEDD(sched, LOGL_ERROR,
+ "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+ msgb_free(msg);
+ return -ENOTSUP;
+ }
+}
diff --git a/src/host/trxcon/src/sched_trx.c b/src/host/trxcon/src/sched_trx.c
new file mode 100644
index 00000000..f4124003
--- /dev/null
+++ b/src/host/trxcon/src/sched_trx.c
@@ -0,0 +1,894 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: GSM PHY routines
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions 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 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.
+ *
+ */
+
+#include <error.h>
+#include <errno.h>
+#include <string.h>
+#include <talloc.h>
+#include <stdbool.h>
+
+#include <osmocom/gsm/a5.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Logging categories to be used for common/data messages */
+int l1sched_log_cat_common = DLGLOBAL;
+int l1sched_log_cat_data = DLGLOBAL;
+
+/* "Dummy" Measurement Report */
+static const uint8_t meas_rep_dummy[] = {
+ /* L1 SACCH pseudo-header */
+ 0x0f, 0x00,
+
+ /* LAPDm header */
+ 0x01, 0x03, 0x49,
+
+ /* RR Management messages, Measurement Report */
+ 0x06, 0x15,
+
+ /* Measurement results (see 3GPP TS 44.018, section 10.5.2.20):
+ * 0... .... = BA-USED: 0
+ * .0.. .... = DTX-USED: DTX was not used
+ * ..11 0110 = RXLEV-FULL-SERVING-CELL: -57 <= x < -56 dBm (54)
+ * 0... .... = 3G-BA-USED: 0
+ * .1.. .... = MEAS-VALID: The measurement results are not valid
+ * ..11 0110 = RXLEV-SUB-SERVING-CELL: -57 <= x < -56 dBm (54)
+ * 0... .... = SI23_BA_USED: 0
+ * .000 .... = RXQUAL-FULL-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0)
+ * .... 000. = RXQUAL-SUB-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0)
+ * .... ...1 11.. .... = NO-NCELL-M: Neighbour cell information not available */
+ 0x36, 0x76, 0x01, 0xc0,
+
+ /* 0** -- Padding with zeroes */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static int l1sched_cfg_pchan_comb_ind(struct l1sched_state *sched,
+ uint8_t tn, enum gsm_phys_chan_config pchan)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->pchan_comb_ind.tn = tn;
+ prim->pchan_comb_ind.pchan = pchan;
+
+ return l1sched_prim_to_user(sched, msg);
+}
+
+static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
+
+/* Pull an Uplink burst from the scheduler and store it to br->burst[].
+ * The TDMA Fn advance must be applied by the caller (if needed).
+ * The given *br must be initialized by the caller. */
+void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br)
+{
+ struct l1sched_ts *ts = sched->ts[br->tn];
+ const struct l1sched_tdma_frame *frame;
+ struct l1sched_lchan_state *lchan;
+ l1sched_lchan_tx_func *handler;
+ enum l1sched_lchan_type chan;
+ unsigned int offset;
+
+ /* Check if the given timeslot is configured */
+ if (ts == NULL || ts->mf_layout == NULL)
+ return;
+
+ /* Get frame from multiframe */
+ offset = br->fn % ts->mf_layout->period;
+ frame = &ts->mf_layout->frames[offset];
+
+ /* Get required info from frame */
+ br->bid = frame->ul_bid;
+ chan = frame->ul_chan;
+ handler = l1sched_lchan_desc[chan].tx_fn;
+
+ /* Omit lchans without handler */
+ if (handler == NULL)
+ return;
+
+ /* Make sure that lchan is allocated and active */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL || !lchan->active)
+ return;
+
+ /* Handover RACH needs to be handled regardless of the
+ * current channel type and the associated handler. */
+ struct msgb *msg = llist_first_entry_or_null(&lchan->tx_prims, struct msgb, list);
+ if (msg && l1sched_prim_type_from_msgb(msg) == L1SCHED_PRIM_T_RACH)
+ handler = l1sched_lchan_desc[L1SCHED_RACH].tx_fn;
+
+ /* Poke lchan handler */
+ handler(lchan, br);
+
+ /* Perform A5/X burst encryption if required */
+ if (lchan->a5.algo)
+ l1sched_a5_burst_enc(lchan, br);
+}
+
+void l1sched_logging_init(int log_cat_common, int log_cat_data)
+{
+ l1sched_log_cat_common = log_cat_common;
+ l1sched_log_cat_data = log_cat_data;
+}
+
+struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv)
+{
+ struct l1sched_state *sched;
+
+ sched = talloc(ctx, struct l1sched_state);
+ if (!sched)
+ return NULL;
+
+ *sched = (struct l1sched_state) {
+ .priv = priv,
+ };
+
+ memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy));
+
+ if (cfg->log_prefix == NULL)
+ sched->log_prefix = talloc_asprintf(sched, "l1sched[0x%p]: ", sched);
+ else
+ sched->log_prefix = talloc_strdup(sched, cfg->log_prefix);
+
+ return sched;
+}
+
+void l1sched_free(struct l1sched_state *sched)
+{
+ unsigned int tn;
+
+ if (sched == NULL)
+ return;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Shutdown scheduler\n");
+
+ /* Free all potentially allocated timeslots */
+ for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++)
+ l1sched_del_ts(sched, tn);
+
+ talloc_free(sched);
+}
+
+void l1sched_reset(struct l1sched_state *sched, bool reset_clock)
+{
+ unsigned int tn;
+
+ if (sched == NULL)
+ return;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Reset scheduler %s\n",
+ reset_clock ? "and clock counter" : "");
+
+ /* Free all potentially allocated timeslots */
+ for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++)
+ l1sched_del_ts(sched, tn);
+
+ memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy));
+}
+
+struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn)
+{
+ /* Make sure that ts isn't allocated yet */
+ if (sched->ts[tn] != NULL) {
+ LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot #%u already allocated\n", tn);
+ return NULL;
+ }
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn);
+
+ sched->ts[tn] = talloc_zero(sched, struct l1sched_ts);
+ sched->ts[tn]->sched = sched;
+ sched->ts[tn]->index = tn;
+
+ return sched->ts[tn];
+}
+
+void l1sched_del_ts(struct l1sched_state *sched, int tn)
+{
+ struct l1sched_lchan_state *lchan, *lchan_next;
+ struct l1sched_ts *ts;
+
+ /* Find ts in list */
+ ts = sched->ts[tn];
+ if (ts == NULL)
+ return;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn);
+
+ /* Deactivate all logical channels */
+ l1sched_deactivate_all_lchans(ts);
+
+ /* Free channel states */
+ llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
+ llist_del(&lchan->list);
+ talloc_free(lchan);
+ }
+
+ /* Remove ts from list and free memory */
+ sched->ts[tn] = NULL;
+ talloc_free(ts);
+
+ /* Notify transceiver about that */
+ l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
+}
+
+#define LAYOUT_HAS_LCHAN(layout, lchan) \
+ (layout->lchan_mask & ((uint64_t) 0x01 << lchan))
+
+int l1sched_configure_ts(struct l1sched_state *sched, int tn,
+ enum gsm_phys_chan_config config)
+{
+ struct l1sched_lchan_state *lchan;
+ enum l1sched_lchan_type type;
+ struct l1sched_ts *ts;
+
+ /* Try to find specified ts */
+ ts = sched->ts[tn];
+ if (ts != NULL) {
+ /* Reconfiguration of existing one */
+ l1sched_reset_ts(sched, tn);
+ } else {
+ /* Allocate a new one if doesn't exist */
+ ts = l1sched_add_ts(sched, tn);
+ if (ts == NULL)
+ return -ENOMEM;
+ }
+
+ /* Choose proper multiframe layout */
+ ts->mf_layout = l1sched_mframe_layout(config, tn);
+ if (!ts->mf_layout)
+ return -EINVAL;
+ if (ts->mf_layout->chan_config != config)
+ return -EINVAL;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE,
+ "(Re)configure TDMA timeslot #%u as %s\n",
+ tn, ts->mf_layout->name);
+
+ /* Init logical channels list */
+ INIT_LLIST_HEAD(&ts->lchans);
+
+ /* Allocate channel states */
+ for (type = 0; type < _L1SCHED_CHAN_MAX; type++) {
+ if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type))
+ continue;
+
+ /* Allocate a channel state */
+ lchan = talloc_zero(ts, struct l1sched_lchan_state);
+ if (!lchan)
+ return -ENOMEM;
+
+ /* set backpointer */
+ lchan->ts = ts;
+
+ /* Set channel type */
+ lchan->type = type;
+
+ /* Init the Tx queue */
+ INIT_LLIST_HEAD(&lchan->tx_prims);
+
+ /* Add to the list of channel states */
+ llist_add_tail(&lchan->list, &ts->lchans);
+
+ /* Enable channel automatically if required */
+ if (l1sched_lchan_desc[type].flags & L1SCHED_CH_FLAG_AUTO)
+ l1sched_activate_lchan(ts, type);
+ }
+
+ /* Notify transceiver about TS activation */
+ l1sched_cfg_pchan_comb_ind(sched, tn, config);
+
+ return 0;
+}
+
+int l1sched_reset_ts(struct l1sched_state *sched, int tn)
+{
+ struct l1sched_lchan_state *lchan, *lchan_next;
+ struct l1sched_ts *ts;
+
+ /* Try to find specified ts */
+ ts = sched->ts[tn];
+ if (ts == NULL)
+ return -EINVAL;
+
+ /* Undefine multiframe layout */
+ ts->mf_layout = NULL;
+
+ /* Deactivate all logical channels */
+ l1sched_deactivate_all_lchans(ts);
+
+ /* Free channel states */
+ llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
+ llist_del(&lchan->list);
+ talloc_free(lchan);
+ }
+
+ /* Notify transceiver about that */
+ l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
+
+ return 0;
+}
+
+int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo,
+ const uint8_t *key, uint8_t key_len)
+{
+ struct l1sched_lchan_state *lchan;
+
+ /* Prevent NULL-pointer deference */
+ if (!ts)
+ return -EINVAL;
+
+ /* Make sure we can store this key */
+ if (key_len > MAX_A5_KEY_LEN)
+ return -ERANGE;
+
+ /* Iterate over all allocated logical channels */
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ /* Omit inactive channels */
+ if (!lchan->active)
+ continue;
+
+ /* Set key length and algorithm */
+ lchan->a5.key_len = key_len;
+ lchan->a5.algo = algo;
+
+ /* Copy requested key */
+ if (key_len)
+ memcpy(lchan->a5.key, key, key_len);
+ }
+
+ return 0;
+}
+
+struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts,
+ enum l1sched_lchan_type type)
+{
+ struct l1sched_lchan_state *lchan;
+
+ llist_for_each_entry(lchan, &ts->lchans, list)
+ if (lchan->type == type)
+ return lchan;
+
+ return NULL;
+}
+
+struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched,
+ uint8_t chan_nr, uint8_t link_id)
+{
+ const struct l1sched_ts *ts = sched->ts[chan_nr & 0x07];
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_lchan_state *lchan;
+
+ if (ts == NULL)
+ return NULL;
+
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+ if (lchan_desc->chan_nr != (chan_nr & RSL_CHAN_NR_MASK))
+ continue;
+ if (lchan_desc->link_id != link_id)
+ continue;
+ return lchan;
+ }
+
+ return NULL;
+}
+
+int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr,
+ int active, uint8_t tch_mode, uint8_t tsc)
+{
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_lchan_state *lchan;
+ int rc = 0;
+
+ /* Prevent NULL-pointer deference */
+ OSMO_ASSERT(ts != NULL);
+
+ /* Iterate over all allocated lchans */
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+
+ if (lchan_desc->chan_nr == (chan_nr & RSL_CHAN_NR_MASK)) {
+ if (active) {
+ rc |= l1sched_activate_lchan(ts, lchan->type);
+ lchan->tch_mode = tch_mode;
+ lchan->tsc = tsc;
+ } else
+ rc |= l1sched_deactivate_lchan(ts, lchan->type);
+ }
+ }
+
+ return rc;
+}
+
+int l1sched_lchan_set_amr_cfg(struct l1sched_lchan_state *lchan,
+ uint8_t codecs_bitmask, uint8_t start_codec)
+{
+ int n = 0;
+ int acum = 0;
+ int pos;
+
+ while ((pos = ffs(codecs_bitmask)) != 0) {
+ acum += pos;
+ LOGP_LCHANC(lchan, LOGL_DEBUG, "AMR codec[%u] = %u\n", n, acum - 1);
+ lchan->amr.codec[n++] = acum - 1;
+ codecs_bitmask >>= pos;
+ }
+ if (n == 0) {
+ LOGP_LCHANC(lchan, LOGL_ERROR, "Empty AMR codec mode bitmask!\n");
+ return -EINVAL;
+ }
+
+ lchan->amr.codecs = n;
+ lchan->amr.dl_ft = start_codec;
+ lchan->amr.dl_cmr = start_codec;
+ lchan->amr.ul_ft = start_codec;
+ lchan->amr.ul_cmr = start_codec;
+
+ return 0;
+}
+
+int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan)
+{
+ const struct l1sched_lchan_desc *lchan_desc = &l1sched_lchan_desc[chan];
+ struct l1sched_lchan_state *lchan;
+
+ /* Try to find requested logical channel */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL)
+ return -EINVAL;
+
+ if (lchan->active) {
+ LOGP_LCHANC(lchan, LOGL_ERROR, "is already activated\n");
+ return -EINVAL;
+ }
+
+ LOGP_LCHANC(lchan, LOGL_NOTICE, "activating\n");
+
+ /* Conditionally allocate memory for bursts */
+ if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) {
+ lchan->rx_bursts = talloc_zero_size(lchan,
+ lchan_desc->burst_buf_size);
+ if (lchan->rx_bursts == NULL)
+ return -ENOMEM;
+ }
+
+ if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) {
+ lchan->tx_bursts = talloc_zero_size(lchan,
+ lchan_desc->burst_buf_size);
+ if (lchan->tx_bursts == NULL)
+ return -ENOMEM;
+ }
+
+ /* Finally, update channel status */
+ lchan->active = 1;
+
+ return 0;
+}
+
+static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan)
+{
+ struct msgb *msg;
+
+ /* Prevent NULL-pointer deference */
+ OSMO_ASSERT(lchan != NULL);
+
+ /* Print some TDMA statistics for Downlink */
+ if (l1sched_lchan_desc[lchan->type].rx_fn && lchan->active) {
+ LOGP_LCHANC(lchan, LOGL_DEBUG, "TDMA statistics: "
+ "%lu DL frames have been processed, "
+ "%lu lost (compensated), last fn=%u\n",
+ lchan->tdma.num_proc,
+ lchan->tdma.num_lost,
+ lchan->tdma.last_proc);
+ }
+
+ /* Reset internal state variables */
+ lchan->rx_burst_mask = 0x00;
+ lchan->tx_burst_mask = 0x00;
+
+ /* Free burst memory */
+ talloc_free(lchan->rx_bursts);
+ talloc_free(lchan->tx_bursts);
+
+ lchan->rx_bursts = NULL;
+ lchan->tx_bursts = NULL;
+
+ /* Flush the queue of pending Tx prims */
+ while ((msg = msgb_dequeue(&lchan->tx_prims)) != NULL) {
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+
+ LOGP_LCHANC(lchan, LOGL_NOTICE, "%s(): dropping Tx prim (fn=%u): %s\n",
+ __func__, prim->data_req.frame_nr, msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ }
+
+ /* Channel specific stuff */
+ if (L1SCHED_CHAN_IS_TCH(lchan->type)) {
+ lchan->dl_ongoing_facch = 0;
+ lchan->ul_facch_blocks = 0;
+
+ lchan->tch_mode = GSM48_CMODE_SIGN;
+
+ /* Reset AMR state */
+ memset(&lchan->amr, 0x00, sizeof(lchan->amr));
+ } else if (L1SCHED_CHAN_IS_SACCH(lchan->type)) {
+ /* Reset SACCH state */
+ memset(&lchan->sacch, 0x00, sizeof(lchan->sacch));
+ }
+
+ /* Reset ciphering state */
+ memset(&lchan->a5, 0x00, sizeof(lchan->a5));
+
+ /* Reset TDMA frame statistics */
+ memset(&lchan->tdma, 0x00, sizeof(lchan->tdma));
+}
+
+int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan)
+{
+ struct l1sched_lchan_state *lchan;
+
+ /* Try to find requested logical channel */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL)
+ return -EINVAL;
+
+ if (!lchan->active) {
+ LOGP_LCHANC(lchan, LOGL_ERROR, "is already deactivated\n");
+ return -EINVAL;
+ }
+
+ LOGP_LCHANC(lchan, LOGL_DEBUG, "deactivating\n");
+
+ /* Reset internal state, free memory */
+ l1sched_reset_lchan(lchan);
+
+ /* Update activation flag */
+ lchan->active = 0;
+
+ return 0;
+}
+
+void l1sched_deactivate_all_lchans(struct l1sched_ts *ts)
+{
+ struct l1sched_lchan_state *lchan;
+
+ LOGP_SCHEDC(ts->sched, LOGL_DEBUG,
+ "Deactivating all logical channels on ts=%d\n",
+ ts->index);
+
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ /* Omit inactive channels */
+ if (!lchan->active)
+ continue;
+
+ /* Reset internal state, free memory */
+ l1sched_reset_lchan(lchan);
+
+ /* Update activation flag */
+ lchan->active = 0;
+ }
+}
+
+enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr)
+{
+ uint8_t cbits = chan_nr >> 3;
+
+ if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
+ return GSM_PCHAN_TCH_F;
+ else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0))
+ return GSM_PCHAN_TCH_H;
+ else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0))
+ return GSM_PCHAN_CCCH_SDCCH4;
+ else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0))
+ return GSM_PCHAN_SDCCH8_SACCH8C;
+ else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4)
+ return GSM_PCHAN_CCCH_SDCCH4_CBCH;
+ else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8)
+ return GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
+ else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH)
+ return GSM_PCHAN_PDCH;
+
+ return GSM_PCHAN_NONE;
+}
+
+static void l1sched_a5_burst_dec(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_ind *bi)
+{
+ ubit_t ks[114];
+ int i;
+
+ /* Generate keystream for a DL burst */
+ osmo_a5(lchan->a5.algo, lchan->a5.key, bi->fn, ks, NULL);
+
+ /* Apply keystream over ciphertext */
+ for (i = 0; i < 57; i++) {
+ if (ks[i])
+ bi->burst[i + 3] *= -1;
+ if (ks[i + 57])
+ bi->burst[i + 88] *= -1;
+ }
+}
+
+static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ ubit_t ks[114];
+ int i;
+
+ /* Generate keystream for an UL burst */
+ osmo_a5(lchan->a5.algo, lchan->a5.key, br->fn, NULL, ks);
+
+ /* Apply keystream over plaintext */
+ for (i = 0; i < 57; i++) {
+ br->burst[i + 3] ^= ks[i];
+ br->burst[i + 88] ^= ks[i + 57];
+ }
+}
+
+static int subst_frame_loss(struct l1sched_lchan_state *lchan,
+ l1sched_lchan_rx_func *handler,
+ uint32_t fn)
+{
+ const struct l1sched_tdma_multiframe *mf;
+ const struct l1sched_tdma_frame *fp;
+ int elapsed, i;
+
+ /* Wait until at least one TDMA frame is processed */
+ if (lchan->tdma.num_proc == 0)
+ return -EAGAIN;
+
+ /* Short alias for the current multiframe */
+ mf = lchan->ts->mf_layout;
+
+ /* Calculate how many frames elapsed since the last received one.
+ * The algorithm is based on GSM::FNDelta() from osmo-trx. */
+ elapsed = fn - lchan->tdma.last_proc;
+ if (elapsed >= GSM_TDMA_HYPERFRAME / 2)
+ elapsed -= GSM_TDMA_HYPERFRAME;
+ else if (elapsed < -GSM_TDMA_HYPERFRAME / 2)
+ elapsed += GSM_TDMA_HYPERFRAME;
+
+ /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */
+ if (elapsed < 0) {
+ /* This burst has already been substituted by a dummy burst (all bits set to zero),
+ * so better drop it. Otherwise we risk to get undefined behavior in handler(). */
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Rx burst with fn=%u older than the last "
+ "processed fn=%u (see OS#4658) => dropping\n",
+ fn, lchan->tdma.last_proc);
+ return -EALREADY;
+ }
+
+ /* Check how many frames we (potentially) need to compensate */
+ if (elapsed > mf->period) {
+ LOGP_LCHANC(lchan, LOGL_NOTICE,
+ "Too many (>%u) contiguous TDMA frames elapsed (%d) "
+ "since the last processed fn=%u (current %u)\n",
+ mf->period, elapsed, lchan->tdma.last_proc, fn);
+ return -EIO;
+ } else if (elapsed == 0) {
+ LOGP_LCHANC(lchan, LOGL_ERROR,
+ "No TDMA frames elapsed since the last processed "
+ "fn=%u, must be a bug?\n", lchan->tdma.last_proc);
+ return -EIO;
+ }
+
+ struct l1sched_burst_ind bi = {
+ .fn = lchan->tdma.last_proc,
+ .tn = lchan->ts->index,
+ .toa256 = 0,
+ .rssi = -120,
+ .burst = { 0 },
+ .burst_len = GSM_NBITS_NB_GMSK_BURST,
+ };
+
+ /* Traverse from fp till the current frame */
+ for (i = 0; i < elapsed - 1; i++) {
+ fp = &mf->frames[GSM_TDMA_FN_INC(bi.fn) % mf->period];
+ if (fp->dl_chan != lchan->type)
+ continue;
+
+ LOGP_LCHANC(lchan, LOGL_NOTICE,
+ "Substituting lost TDMA frame fn=%u\n", bi.fn);
+
+ bi.bid = fp->dl_bid;
+ handler(lchan, &bi);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = bi.fn;
+ lchan->tdma.num_proc++;
+ lchan->tdma.num_lost++;
+ }
+
+ return 0;
+}
+
+int l1sched_handle_rx_burst(struct l1sched_state *sched,
+ struct l1sched_burst_ind *bi)
+{
+ struct l1sched_lchan_state *lchan;
+ const struct l1sched_tdma_frame *frame;
+ struct l1sched_ts *ts = sched->ts[bi->tn];
+
+ l1sched_lchan_rx_func *handler;
+ enum l1sched_lchan_type chan;
+ uint8_t offset;
+ int rc;
+
+ /* Check whether required timeslot is allocated and configured */
+ if (ts == NULL || ts->mf_layout == NULL) {
+ LOGP_SCHEDD(sched, LOGL_DEBUG,
+ "Timeslot #%u isn't configured, ignoring burst...\n", bi->tn);
+ return -EINVAL;
+ }
+
+ /* Get frame from multiframe */
+ offset = bi->fn % ts->mf_layout->period;
+ frame = ts->mf_layout->frames + offset;
+
+ /* Get required info from frame */
+ bi->bid = frame->dl_bid;
+ chan = frame->dl_chan;
+ handler = l1sched_lchan_desc[chan].rx_fn;
+
+ /* Omit bursts which have no handler, like IDLE bursts.
+ * TODO: handle noise indications during IDLE frames. */
+ if (!handler)
+ return -ENODEV;
+
+ /* Find required channel state */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL)
+ return -ENODEV;
+
+ /* Ensure that channel is active */
+ if (!lchan->active)
+ return 0;
+
+ /* Compensate lost TDMA frames (if any) */
+ rc = subst_frame_loss(lchan, handler, bi->fn);
+ if (rc == -EALREADY)
+ return rc;
+
+ /* Perform A5/X decryption if required */
+ if (lchan->a5.algo)
+ l1sched_a5_burst_dec(lchan, bi);
+
+ /* Put burst to handler */
+ handler(lchan, bi);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = bi->fn;
+
+ if (++lchan->tdma.num_proc == 0) {
+ /* Theoretically, we may have an integer overflow of num_proc counter.
+ * As a consequence, subst_frame_loss() will be unable to compensate
+ * one (potentionally lost) Downlink burst. On practice, it would
+ * happen once in 4615 * 10e-6 * (2 ^ 32 - 1) seconds or ~6 years. */
+ LOGP_LCHAND(lchan, LOGL_NOTICE,
+ "Too many TDMA frames have been processed. "
+ "Are you running trxcon for more than 6 years?!?\n");
+ lchan->tdma.num_proc = 1;
+ }
+
+ return 0;
+}
+
+int l1sched_handle_rx_probe(struct l1sched_state *sched,
+ struct l1sched_probe *probe)
+{
+ struct l1sched_ts *ts = sched->ts[probe->tn];
+ const struct l1sched_tdma_frame *frame;
+ struct l1sched_lchan_state *lchan;
+ unsigned int offset;
+
+ /* Check whether required timeslot is allocated and configured */
+ if (ts == NULL || ts->mf_layout == NULL)
+ return -EINVAL;
+
+ /* Get frame from multiframe */
+ offset = probe->fn % ts->mf_layout->period;
+ frame = &ts->mf_layout->frames[offset];
+
+ if (l1sched_lchan_desc[frame->dl_chan].rx_fn == NULL)
+ return -ENODEV;
+
+ /* Find the appropriate logical channel */
+ lchan = l1sched_find_lchan_by_type(ts, frame->dl_chan);
+ if (lchan == NULL)
+ return -ENODEV;
+
+ if (lchan->active)
+ probe->flags |= L1SCHED_PROBE_F_ACTIVE;
+
+ return 0;
+}
+
+#define MEAS_HIST_FIRST(hist) \
+ (&hist->buf[0])
+#define MEAS_HIST_LAST(hist) \
+ (MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1)
+
+/* Add a new set of measurements to the history */
+void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ struct l1sched_lchan_meas_hist *hist = &lchan->meas_hist;
+
+ /* Find a new position where to store the measurements */
+ if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL)
+ hist->head = MEAS_HIST_FIRST(hist);
+ else
+ hist->head++;
+
+ *hist->head = (struct l1sched_meas_set) {
+ .fn = bi->fn,
+ .toa256 = bi->toa256,
+ .rssi = bi->rssi,
+ };
+}
+
+/* Calculate the AVG of n measurements from the history */
+void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n)
+{
+ struct l1sched_lchan_meas_hist *hist = &lchan->meas_hist;
+ struct l1sched_meas_set *meas = hist->head;
+ int toa256_sum = 0;
+ int rssi_sum = 0;
+ int i;
+
+ OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf));
+ OSMO_ASSERT(meas != NULL);
+
+ /* Traverse backwards up to n entries, calculate the sum */
+ for (i = 0; i < n; i++) {
+ toa256_sum += meas->toa256;
+ rssi_sum += meas->rssi;
+
+ /* Do not go below the first burst */
+ if (i + 1 == n)
+ break;
+
+ if (meas == MEAS_HIST_FIRST(hist))
+ meas = MEAS_HIST_LAST(hist);
+ else
+ meas--;
+ }
+
+ /* Calculate the AVG */
+ lchan->meas_avg.toa256 = toa256_sum / n;
+ lchan->meas_avg.rssi = rssi_sum / n;
+
+ /* As a bonus, store TDMA frame number of the first burst */
+ lchan->meas_avg.fn = meas->fn;
+}
diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/src/trx_if.c
index 8dbbd128..fad10264 100644
--- a/src/host/trxcon/trx_if.c
+++ b/src/host/trxcon/src/trx_if.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -38,12 +39,17 @@
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
-#include "l1ctl.h"
-#include "trxcon.h"
-#include "trx_if.h"
-#include "logging.h"
-#include "scheduler.h"
+#include <osmocom/bb/trxcon/trx_if.h>
+#include <osmocom/bb/trxcon/logging.h>
+
+#define TRXDv0_HDR_LEN 8
+
+#define S(x) (1 << (x))
+
+static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause);
static struct value_string trx_evt_names[] = {
{ 0, NULL } /* no events? */
@@ -52,8 +58,8 @@ static struct value_string trx_evt_names[] = {
static struct osmo_fsm_state trx_fsm_states[] = {
[TRX_STATE_OFFLINE] = {
.out_state_mask = (
- GEN_MASK(TRX_STATE_IDLE) |
- GEN_MASK(TRX_STATE_RSP_WAIT)),
+ S(TRX_STATE_IDLE) |
+ S(TRX_STATE_RSP_WAIT)),
.name = "OFFLINE",
},
[TRX_STATE_IDLE] = {
@@ -62,25 +68,26 @@ static struct osmo_fsm_state trx_fsm_states[] = {
},
[TRX_STATE_ACTIVE] = {
.out_state_mask = (
- GEN_MASK(TRX_STATE_IDLE) |
- GEN_MASK(TRX_STATE_RSP_WAIT)),
+ S(TRX_STATE_IDLE) |
+ S(TRX_STATE_RSP_WAIT)),
.name = "ACTIVE",
},
[TRX_STATE_RSP_WAIT] = {
.out_state_mask = (
- GEN_MASK(TRX_STATE_IDLE) |
- GEN_MASK(TRX_STATE_ACTIVE) |
- GEN_MASK(TRX_STATE_OFFLINE)),
+ S(TRX_STATE_IDLE) |
+ S(TRX_STATE_ACTIVE) |
+ S(TRX_STATE_OFFLINE)),
.name = "RSP_WAIT",
},
};
static struct osmo_fsm trx_fsm = {
- .name = "trx_interface_fsm",
+ .name = "trx_interface",
.states = trx_fsm_states,
.num_states = ARRAY_SIZE(trx_fsm_states),
- .log_subsys = DTRX,
+ .log_subsys = DTRXC,
.event_names = trx_evt_names,
+ .cleanup = &trx_fsm_cleanup_cb,
};
static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host_local,
@@ -143,13 +150,13 @@ static void trx_ctrl_send(struct trx_instance *trx)
tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list);
/* Send command */
- LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd);
+ LOGPFSML(trx->fi, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd);
send(trx->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd) + 1, 0);
/* Trigger state machine */
- if (trx->fsm->state != TRX_STATE_RSP_WAIT) {
- trx->prev_state = trx->fsm->state;
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_RSP_WAIT, 0, 0);
+ if (trx->fi->state != TRX_STATE_RSP_WAIT) {
+ trx->prev_state = trx->fi->state;
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_RSP_WAIT, 0, 0);
}
/* Start expire timer */
@@ -167,13 +174,13 @@ static void trx_ctrl_timer_cb(void *data)
if (llist_empty(&trx->trx_ctrl_list))
return;
- LOGP(DTRX, LOGL_NOTICE, "No response from transceiver...\n");
+ LOGPFSML(trx->fi, LOGL_NOTICE, "No response from transceiver...\n");
tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list);
if (++tcm->retry_cnt > 3) {
- LOGP(DTRX, LOGL_NOTICE, "Transceiver offline\n");
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_OFFLINE, 0, 0);
- osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_OFFLINE, trx);
+ LOGPFSML(trx->fi, LOGL_NOTICE, "Transceiver offline\n");
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_OFFLINE, 0, 0);
+ osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_TIMEOUT, NULL);
return;
}
@@ -212,7 +219,7 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical,
tcm->cmd_len = strlen(cmd);
tcm->critical = critical;
llist_add_tail(&tcm->list, &trx->trx_ctrl_list);
- LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
+ LOGPFSML(trx->fi, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
/* Send message, if no pending messages */
if (!pending)
@@ -242,23 +249,22 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical,
* RSP POWERON <status>
*/
-int trx_if_cmd_echo(struct trx_instance *trx)
+static int trx_if_cmd_echo(struct trx_instance *trx)
{
return trx_ctrl_cmd(trx, 1, "ECHO", "");
}
-int trx_if_cmd_poweroff(struct trx_instance *trx)
+static int trx_if_cmd_poweroff(struct trx_instance *trx)
{
return trx_ctrl_cmd(trx, 1, "POWEROFF", "");
}
-int trx_if_cmd_poweron(struct trx_instance *trx)
+static int trx_if_cmd_poweron(struct trx_instance *trx)
{
- if (trx->powered_up) {
- /* FIXME: this should be handled by the FSM, not here! */
- LOGP(DTRX, LOGL_ERROR, "Suppressing POWERON as we're already powered up\n");
+#if 0
+ if (trx->powered_up)
return -EAGAIN;
- }
+#endif
return trx_ctrl_cmd(trx, 1, "POWERON", "");
}
@@ -273,9 +279,25 @@ int trx_if_cmd_poweron(struct trx_instance *trx)
* RSP SETSLOT <status> <timeslot> <chantype>
*/
-int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type)
+static int trx_if_cmd_setslot(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setslot *cmdp)
{
- return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u", tn, type);
+ /* Values correspond to 'enum ChannelCombination' in osmo-trx.git */
+ static const uint8_t chan_types[_GSM_PCHAN_MAX] = {
+ [GSM_PCHAN_UNKNOWN] = 0,
+ [GSM_PCHAN_NONE] = 0,
+ [GSM_PCHAN_CCCH] = 4,
+ [GSM_PCHAN_CCCH_SDCCH4] = 5,
+ [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 5,
+ [GSM_PCHAN_TCH_F] = 1,
+ [GSM_PCHAN_TCH_H] = 3,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = 7,
+ [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 7,
+ [GSM_PCHAN_PDCH] = 13,
+ };
+
+ return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u",
+ cmdp->tn, chan_types[cmdp->pchan]);
}
/*
@@ -290,28 +312,30 @@ int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type)
* RSP (RX/TX)TUNE <status> <kHz>
*/
-int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn)
+static int trx_if_cmd_rxtune(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setfreq_h0 *cmdp)
{
uint16_t freq10;
/* RX is downlink on MS side */
- freq10 = gsm_arfcn2freq10(band_arfcn, 0);
+ freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 0);
if (freq10 == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn);
+ LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", cmdp->band_arfcn);
return -ENOTSUP;
}
return trx_ctrl_cmd(trx, 1, "RXTUNE", "%u", freq10 * 100);
}
-int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn)
+static int trx_if_cmd_txtune(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setfreq_h0 *cmdp)
{
uint16_t freq10;
/* TX is uplink on MS side */
- freq10 = gsm_arfcn2freq10(band_arfcn, 1);
+ freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 1);
if (freq10 == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn);
+ LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", cmdp->band_arfcn);
return -ENOTSUP;
}
@@ -330,19 +354,16 @@ int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn)
* RSP MEASURE <status> <kHz> <dB>
*/
-int trx_if_cmd_measure(struct trx_instance *trx,
- uint16_t band_arfcn_start, uint16_t band_arfcn_stop)
+static int trx_if_cmd_measure(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_measure *cmdp)
{
uint16_t freq10;
- /* Update ARFCN range for measurement */
- trx->pm_band_arfcn_start = band_arfcn_start;
- trx->pm_band_arfcn_stop = band_arfcn_stop;
-
/* Calculate a frequency for current ARFCN (DL) */
- freq10 = gsm_arfcn2freq10(band_arfcn_start, 0);
+ freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 0);
if (freq10 == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn_start);
+ LOGPFSML(trx->fi, LOGL_ERROR,
+ "ARFCN %d not defined\n", cmdp->band_arfcn);
return -ENOTSUP;
}
@@ -359,23 +380,22 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp)
sscanf(resp, "%u %d", &freq10, &dbm);
freq10 /= 100;
- /* Check received ARFCN against expected */
band_arfcn = gsm_freq102arfcn((uint16_t) freq10, 0);
- if (band_arfcn != trx->pm_band_arfcn_start) {
- LOGP(DTRX, LOGL_ERROR, "Power measurement error: "
- "response ARFCN=%u doesn't match expected ARFCN=%u\n",
- band_arfcn &~ ARFCN_FLAG_MASK,
- trx->pm_band_arfcn_start &~ ARFCN_FLAG_MASK);
+ if (band_arfcn == 0xffff) {
+ LOGPFSML(trx->fi, LOGL_ERROR,
+ "Failed to parse ARFCN from RSP MEASURE: %s\n", resp);
return;
}
- /* Send L1CTL_PM_CONF */
- l1ctl_tx_pm_conf(trx->l1l, band_arfcn, dbm,
- band_arfcn == trx->pm_band_arfcn_stop);
+ const struct trxcon_phyif_rsp rsp = {
+ .type = TRXCON_PHYIF_CMDT_MEASURE,
+ .param.measure = {
+ .band_arfcn = band_arfcn,
+ .dbm = dbm,
+ },
+ };
- /* Schedule a next measurement */
- if (band_arfcn != trx->pm_band_arfcn_stop)
- trx_if_cmd_measure(trx, ++band_arfcn, trx->pm_band_arfcn_stop);
+ trxcon_phyif_handle_rsp(trx->priv, &rsp);
}
/*
@@ -391,9 +411,10 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp)
* RSP SETTA <status> <TA>
*/
-int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta)
+static int trx_if_cmd_setta(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setta *cmdp)
{
- return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta);
+ return trx_ctrl_cmd(trx, 0, "SETTA", "%d", cmdp->ta);
}
/*
@@ -409,8 +430,8 @@ int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta)
* channel list is expected to be sorted in ascending order.
*/
-int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn,
- uint8_t maio, uint16_t *ma, size_t ma_len)
+static int trx_if_cmd_setfh(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setfreq_h1 *cmdp)
{
/* Reserve some room for CMD SETFH <HSN> <MAIO> */
char ma_buf[TRXC_BUF_SIZE - 24];
@@ -420,28 +441,28 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn,
int i, rc;
/* Make sure that Mobile Allocation has at least one ARFCN */
- if (!ma_len || ma == NULL) {
- LOGP(DTRX, LOGL_ERROR, "Mobile Allocation is empty?!?\n");
+ if (!cmdp->ma_len || cmdp->ma == NULL) {
+ LOGPFSML(trx->fi, LOGL_ERROR, "Mobile Allocation is empty?!?\n");
return -EINVAL;
}
/* Compose a sequence of Rx/Tx frequencies (mobile allocation) */
- for (i = 0, ptr = ma_buf; i < ma_len; i++) {
+ for (i = 0, ptr = ma_buf; i < cmdp->ma_len; i++) {
/* Convert ARFCN to a pair of Rx/Tx frequencies (Hz * 10) */
- rx_freq = gsm_arfcn2freq10(ma[i], 0); /* Rx: Downlink */
- tx_freq = gsm_arfcn2freq10(ma[i], 1); /* Tx: Uplink */
+ rx_freq = gsm_arfcn2freq10(cmdp->ma[i], 0); /* Rx: Downlink */
+ tx_freq = gsm_arfcn2freq10(cmdp->ma[i], 1); /* Tx: Uplink */
if (rx_freq == 0xffff || tx_freq == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "Failed to convert ARFCN %u "
+ LOGPFSML(trx->fi, LOGL_ERROR, "Failed to convert ARFCN %u "
"to a pair of Rx/Tx frequencies\n",
- ma[i] & ~ARFCN_FLAG_MASK);
+ cmdp->ma[i] & ~ARFCN_FLAG_MASK);
return -EINVAL;
}
/* Append a pair of Rx/Tx frequencies (in kHz) to the buffer */
rc = snprintf(ptr, ma_buf_len, "%u %u ", rx_freq * 100, tx_freq * 100);
if (rc < 0 || rc > ma_buf_len) { /* Prevent buffer overflow */
- LOGP(DTRX, LOGL_ERROR, "Not enough room to encode "
- "Mobile Allocation (N=%zu)\n", ma_len);
+ LOGPFSML(trx->fi, LOGL_ERROR, "Not enough room to encode "
+ "Mobile Allocation (N=%u)\n", cmdp->ma_len);
return -ENOSPC;
}
@@ -453,7 +474,7 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn,
/* Overwrite the last space */
*(ptr - 1) = '\0';
- return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", hsn, maio, ma_buf);
+ return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", cmdp->hsn, cmdp->maio, ma_buf);
}
/* Get response from CTRL socket */
@@ -467,13 +488,13 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
read_len = read(ofd->fd, buf, sizeof(buf) - 1);
if (read_len <= 0) {
- LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
+ LOGPFSML(trx->fi, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
return read_len;
}
buf[read_len] = '\0';
if (!!strncmp(buf, "RSP ", 4)) {
- LOGP(DTRX, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf);
+ LOGPFSML(trx->fi, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf);
return 0;
}
@@ -481,15 +502,14 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
p = strchr(buf + 4, ' ');
rsp_len = p ? p - buf - 4 : strlen(buf) - 4;
- LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf);
+ LOGPFSML(trx->fi, LOGL_INFO, "Response message: '%s'\n", buf);
/* Abort expire timer */
- if (osmo_timer_pending(&trx->trx_ctrl_timer))
- osmo_timer_del(&trx->trx_ctrl_timer);
+ osmo_timer_del(&trx->trx_ctrl_timer);
/* Get command for response message */
if (llist_empty(&trx->trx_ctrl_list)) {
- LOGP(DTRX, LOGL_NOTICE, "Response message without command\n");
+ LOGPFSML(trx->fi, LOGL_NOTICE, "Response message without command\n");
return -EINVAL;
}
@@ -498,7 +518,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* Check if response matches command */
if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) {
- LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
+ LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
"Response message '%s' does not match command "
"message '%s'\n", buf, tcm->cmd);
goto rsp_error;
@@ -507,7 +527,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* Check for response code */
sscanf(p + 1, "%d", &resp);
if (resp) {
- LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
+ LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
"Transceiver rejected TRX command with "
"response: '%s'\n", buf);
@@ -518,18 +538,18 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* Trigger state machine */
if (!strncmp(tcm->cmd + 4, "POWERON", 7)) {
trx->powered_up = true;
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_ACTIVE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_ACTIVE, 0, 0);
}
else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) {
trx->powered_up = false;
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
}
else if (!strncmp(tcm->cmd + 4, "MEASURE", 7))
trx_if_measure_rsp_cb(trx, buf + 14);
else if (!strncmp(tcm->cmd + 4, "ECHO", 4))
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
else
- osmo_fsm_inst_state_chg(trx->fsm, trx->prev_state, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, trx->prev_state, 0, 0);
/* Remove command from list */
llist_del(&tcm->list);
@@ -541,11 +561,53 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
rsp_error:
- /* Notify higher layers about the problem */
- osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_RSP_ERROR, trx);
+ osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_ERROR, NULL);
return -EIO;
}
+int trx_if_handle_phyif_cmd(struct trx_instance *trx, const struct trxcon_phyif_cmd *cmd)
+{
+ int rc;
+
+ switch (cmd->type) {
+ case TRXCON_PHYIF_CMDT_RESET:
+ if ((rc = trx_if_cmd_poweroff(trx)) != 0)
+ return rc;
+ rc = trx_if_cmd_echo(trx);
+ break;
+ case TRXCON_PHYIF_CMDT_POWERON:
+ rc = trx_if_cmd_poweron(trx);
+ break;
+ case TRXCON_PHYIF_CMDT_POWEROFF:
+ rc = trx_if_cmd_poweroff(trx);
+ break;
+ case TRXCON_PHYIF_CMDT_MEASURE:
+ rc = trx_if_cmd_measure(trx, &cmd->param.measure);
+ break;
+ case TRXCON_PHYIF_CMDT_SETFREQ_H0:
+ if ((rc = trx_if_cmd_rxtune(trx, &cmd->param.setfreq_h0)) != 0)
+ return rc;
+ if ((rc = trx_if_cmd_txtune(trx, &cmd->param.setfreq_h0)) != 0)
+ return rc;
+ break;
+ case TRXCON_PHYIF_CMDT_SETFREQ_H1:
+ rc = trx_if_cmd_setfh(trx, &cmd->param.setfreq_h1);
+ break;
+ case TRXCON_PHYIF_CMDT_SETSLOT:
+ rc = trx_if_cmd_setslot(trx, &cmd->param.setslot);
+ break;
+ case TRXCON_PHYIF_CMDT_SETTA:
+ rc = trx_if_cmd_setta(trx, &cmd->param.setta);
+ break;
+ default:
+ LOGPFSML(trx->fi, LOGL_ERROR,
+ "Unhandled PHYIF command type=0x%02x\n", cmd->type);
+ rc = -ENODEV;
+ }
+
+ return rc;
+}
+
/* ------------------------------------------------------------------------ */
/* Data interface handlers */
/* ------------------------------------------------------------------------ */
@@ -571,66 +633,90 @@ rsp_error:
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_instance *trx = ofd->data;
- struct trx_meas_set meas;
+ struct trxcon_phyif_burst_ind bi;
uint8_t buf[TRXD_BUF_SIZE];
- sbit_t bits[148];
- int8_t rssi, tn;
- int16_t toa256;
- uint32_t fn;
ssize_t read_len;
+ sbit_t *burst;
read_len = read(ofd->fd, buf, sizeof(buf));
if (read_len <= 0) {
- LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
+ strerror_r(errno, (char *)buf, sizeof(buf));
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "read() failed on TRXD with rc=%zd (%s)\n",
+ read_len, (const char *)buf);
return read_len;
}
- if (read_len < (8 + 148)) { /* TRXDv0 header + GMSK burst */
- LOGP(DTRXD, LOGL_ERROR, "Got data message with invalid "
- "length '%zd'\n", read_len);
+ if (read_len < TRXDv0_HDR_LEN) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Got malformed TRXD PDU (short length=%zd)\n", read_len);
return -EINVAL;
}
- tn = buf[0];
- fn = osmo_load32be(buf + 1);
- rssi = -(int8_t) buf[5];
- toa256 = ((int16_t) (buf[6] << 8) | buf[7]);
-
- /* Copy and convert bits {254..0} to sbits {-127..127} */
- osmo_ubit2sbit(bits, buf + 8, 148);
+ if ((buf[0] >> 4) != 0) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Got TRXD PDU with unexpected version\n");
+ return -ENOTSUP;
+ }
- if (tn >= 8) {
- LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn);
+ read_len -= TRXDv0_HDR_LEN;
+ switch (read_len) {
+ /* TRXDv0 PDUs may have 2 dummy bytes at the end */
+ case GSM_NBITS_NB_GMSK_BURST + 2:
+ case GSM_NBITS_NB_8PSK_BURST + 2:
+ read_len -= 2;
+ break;
+ case GSM_NBITS_NB_GMSK_BURST:
+ case GSM_NBITS_NB_8PSK_BURST:
+ break;
+ default:
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Got TRXD PDU unexpected burst length=%zd\n", read_len);
return -EINVAL;
}
- if (fn >= 2715648) {
- LOGP(DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn);
+ burst = (sbit_t *)&buf[8];
+
+ bi = (struct trxcon_phyif_burst_ind) {
+ .tn = buf[0] & 0x07,
+ .fn = osmo_load32be(buf + 1),
+ .rssi = -(int8_t) buf[5],
+ .toa256 = (int16_t) (buf[6] << 8) | buf[7],
+ .burst = burst, /* at least GSM_NBITS_NB_GMSK_BURST */
+ .burst_len = read_len,
+ };
+
+ /* Convert ubits {254..0} to sbits {-127..127} in-place */
+ for (unsigned int i = 0; i < bi.burst_len; i++) {
+ if (buf[8 + i] == 255)
+ burst[i] = -127;
+ else
+ burst[i] = 127 - buf[8 + i];
+ }
+
+ if (bi.fn >= GSM_TDMA_HYPERFRAME) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal FN %u\n", bi.fn);
return -EINVAL;
}
- LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
- tn, fn, rssi, toa256);
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
+ "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
+ bi.tn, bi.fn, bi.rssi, bi.toa256);
- /* Group the measurements together */
- meas = (struct trx_meas_set) {
- .toa256 = toa256,
- .rssi = rssi,
- .fn = fn,
- };
+ trxcon_phyif_handle_burst_ind(trx->priv, &bi);
- /* Poke scheduler */
- sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, &meas);
+ struct trxcon_phyif_rts_ind rts = {
+ .fn = GSM_TDMA_FN_SUM(bi.fn, trx->fn_advance),
+ .tn = bi.tn,
+ };
- /* Correct local clock counter */
- if (fn % 51 == 0)
- sched_clck_handle(&trx->sched, fn);
+ trxcon_phyif_handle_rts_ind(trx->priv, &rts);
return 0;
}
-int trx_if_tx_burst(struct trx_instance *trx,
- const struct sched_burst_req *br)
+int trx_if_handle_phyif_burst_req(struct trx_instance *trx,
+ const struct trxcon_phyif_burst_req *br)
{
uint8_t buf[TRXD_BUF_SIZE];
size_t length;
@@ -643,15 +729,16 @@ int trx_if_tx_burst(struct trx_instance *trx,
* transceiver and its TRXC interface.
*/
#if 0
- if (trx->fsm->state != TRX_STATE_ACTIVE) {
- LOGP(DTRXD, LOGL_ERROR, "Ignoring TX data, "
- "transceiver isn't ready\n");
+ if (trx->fi->state != TRX_STATE_ACTIVE) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Ignoring TX data, transceiver isn't ready\n");
return -EAGAIN;
}
#endif
- LOGP(DTRXD, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n",
- br->tn, br->fn, br->pwr);
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
+ "TX burst tn=%u fn=%u pwr=%u\n",
+ br->tn, br->fn, br->pwr);
buf[0] = br->tn;
osmo_store32be(br->fn, buf + 1);
@@ -671,30 +758,32 @@ int trx_if_tx_burst(struct trx_instance *trx,
}
/* Init TRX interface (TRXC, TRXD sockets and FSM) */
-struct trx_instance *trx_if_open(void *tall_ctx,
- const char *local_host, const char *remote_host,
- uint16_t base_port)
+struct trx_instance *trx_if_open(const struct trx_if_params *params)
{
+ const unsigned int offset = params->instance * 2;
struct trx_instance *trx;
+ struct osmo_fsm_inst *fi;
int rc;
- LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface "
- "(%s:%u)\n", remote_host, base_port);
+ LOGPFSML(params->parent_fi, LOGL_NOTICE,
+ "Init transceiver interface (%s:%u/%u)\n",
+ params->remote_host, params->base_port,
+ params->instance);
- /* Try to allocate memory */
- trx = talloc_zero(tall_ctx, struct trx_instance);
- if (!trx) {
- LOGP(DTRX, LOGL_ERROR, "Failed to allocate memory\n");
+ /* Allocate a new dedicated state machine */
+ fi = osmo_fsm_inst_alloc_child(&trx_fsm, params->parent_fi,
+ params->parent_term_event);
+ if (fi == NULL) {
+ LOGPFSML(params->parent_fi, LOGL_ERROR,
+ "Failed to allocate an instance of FSM '%s'\n",
+ trx_fsm.name);
return NULL;
}
- /* Allocate a new dedicated state machine */
- trx->fsm = osmo_fsm_inst_alloc(&trx_fsm, trx,
- NULL, LOGL_DEBUG, "trx_interface");
- if (trx->fsm == NULL) {
- LOGP(DTRX, LOGL_ERROR, "Failed to allocate an instance "
- "of FSM '%s'\n", trx_fsm.name);
- talloc_free(trx);
+ trx = talloc_zero(fi, struct trx_instance);
+ if (!trx) {
+ LOGPFSML(params->parent_fi, LOGL_ERROR, "Failed to allocate memory\n");
+ osmo_fsm_inst_free(fi);
return NULL;
}
@@ -702,32 +791,40 @@ struct trx_instance *trx_if_open(void *tall_ctx,
INIT_LLIST_HEAD(&trx->trx_ctrl_list);
/* Open sockets */
- rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, local_host,
- base_port + 101, remote_host, base_port + 1, trx_ctrl_read_cb);
+ rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, /* TRXC */
+ params->local_host, params->base_port + 101 + offset,
+ params->remote_host, params->base_port + 1 + offset,
+ trx_ctrl_read_cb);
if (rc < 0)
goto udp_error;
- rc = trx_udp_open(trx, &trx->trx_ofd_data, local_host,
- base_port + 102, remote_host, base_port + 2, trx_data_rx_cb);
+ rc = trx_udp_open(trx, &trx->trx_ofd_data, /* TRXD */
+ params->local_host, params->base_port + 102 + offset,
+ params->remote_host, params->base_port + 2 + offset,
+ trx_data_rx_cb);
if (rc < 0)
goto udp_error;
+ trx->fn_advance = params->fn_advance;
+ trx->priv = params->priv;
+ fi->priv = trx;
+ trx->fi = fi;
+
return trx;
udp_error:
- LOGP(DTRX, LOGL_ERROR, "Couldn't establish UDP connection\n");
- osmo_fsm_inst_free(trx->fsm);
- talloc_free(trx);
+ LOGPFSML(params->parent_fi, LOGL_ERROR, "Couldn't establish UDP connection\n");
+ osmo_fsm_inst_free(trx->fi);
return NULL;
}
/* Flush pending control messages */
-void trx_if_flush_ctrl(struct trx_instance *trx)
+static void trx_if_flush_ctrl(struct trx_instance *trx)
{
struct trx_ctrl_msg *tcm;
/* Reset state machine */
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
/* Clear command queue */
while (!llist_empty(&trx->trx_ctrl_list)) {
@@ -740,21 +837,39 @@ void trx_if_flush_ctrl(struct trx_instance *trx)
void trx_if_close(struct trx_instance *trx)
{
+ if (trx == NULL || trx->fi == NULL)
+ return;
+ osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_REQUEST, NULL);
+}
+
+static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ static const char cmd_poweroff[] = "CMD POWEROFF";
+ struct trx_instance *trx = fi->priv;
+
/* May be unallocated due to init error */
if (!trx)
return;
- LOGP(DTRX, LOGL_NOTICE, "Shutdown transceiver interface\n");
+ LOGPFSML(fi, LOGL_NOTICE, "Shutdown transceiver interface\n");
+
+ /* Abort TRXC response timer (if pending) */
+ osmo_timer_del(&trx->trx_ctrl_timer);
/* Flush CTRL message list */
trx_if_flush_ctrl(trx);
+ /* Power off if the transceiver is up */
+ if (trx->powered_up && trx->trx_ofd_ctrl.fd >= 0)
+ send(trx->trx_ofd_ctrl.fd, &cmd_poweroff[0], sizeof(cmd_poweroff), 0);
+
/* Close sockets */
trx_udp_close(&trx->trx_ofd_ctrl);
trx_udp_close(&trx->trx_ofd_data);
/* Free memory */
- osmo_fsm_inst_free(trx->fsm);
+ trx->fi->priv = NULL;
talloc_free(trx);
}
diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c
new file mode 100644
index 00000000..95458838
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_fsm.c
@@ -0,0 +1,822 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * The trxcon state machine (see 3GPP TS 44.004, section 5.1)
+ *
+ * (C) 2022-2023 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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/phyif.h>
+#include <osmocom/bb/trxcon/l1ctl.h>
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1gprs.h>
+
+#define S(x) (1 << (x))
+
+static void trxcon_allstate_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ struct trxcon_phyif_cmd phycmd = { };
+
+ switch (event) {
+ case TRXCON_EV_PHYIF_FAILURE:
+ trxcon->phyif = NULL;
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ break;
+ case TRXCON_EV_L2IF_FAILURE:
+ trxcon->l2if = NULL;
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ break;
+ case TRXCON_EV_RESET_FULL_REQ:
+ TALLOC_FREE(trxcon->fi_data);
+ if (fi->state != TRXCON_ST_RESET)
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
+ l1sched_reset(trxcon->sched, true);
+
+ /* Reset the L1 parameters */
+ trxcon->l1p.band_arfcn = 0xffff;
+ trxcon->l1p.tx_power = 0;
+ trxcon->l1p.ta = 0;
+
+ phycmd.type = TRXCON_PHYIF_CMDT_RESET;
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ break;
+ case TRXCON_EV_RESET_SCHED_REQ:
+ l1sched_reset(trxcon->sched, false);
+ break;
+ case TRXCON_EV_SET_PHY_CONFIG_REQ:
+ {
+ const struct trxcon_param_set_phy_config_req *req = data;
+
+ switch (req->type) {
+ case TRXCON_PHY_CFGT_PCHAN_COMB:
+ phycmd.type = TRXCON_PHYIF_CMDT_SETSLOT;
+ phycmd.param.setslot.tn = req->pchan_comb.tn;
+ phycmd.param.setslot.pchan = req->pchan_comb.pchan;
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ break;
+ case TRXCON_PHY_CFGT_TX_PARAMS:
+ if (trxcon->l1p.ta != req->tx_params.timing_advance) {
+ phycmd.type = TRXCON_PHYIF_CMDT_SETTA;
+ phycmd.param.setta.ta = req->tx_params.timing_advance;
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ }
+ trxcon->l1p.tx_power = req->tx_params.tx_power;
+ trxcon->l1p.ta = req->tx_params.timing_advance;
+ break;
+ }
+ break;
+ }
+ case TRXCON_EV_UPDATE_SACCH_CACHE_REQ:
+ {
+ const struct trxcon_param_tx_data_req *req = data;
+
+ if (req->link_id != L1SCHED_CH_LID_SACCH) {
+ LOGPFSML(fi, LOGL_ERROR, "Unexpected link_id=0x%02x\n", req->link_id);
+ break;
+ }
+ if (req->data_len != GSM_MACBLOCK_LEN) {
+ LOGPFSML(fi, LOGL_ERROR, "Unexpected data length=%zu\n", req->data_len);
+ break;
+ }
+
+ memcpy(&trxcon->sched->sacch_cache[0], req->data, req->data_len);
+ break;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int trxcon_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (fi->state) {
+ case TRXCON_ST_FBSB_SEARCH:
+ l1ctl_tx_fbsb_fail(trxcon, trxcon->l1p.band_arfcn);
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
+ return 0;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_full_power_scan_req(struct osmo_fsm_inst *fi,
+ const struct trxcon_param_full_power_scan_req *req)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ enum gsm_band band_start, band_stop;
+
+ if (trxcon->fi_data != NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "Full power scan is already in progress\n");
+ return;
+ }
+
+ /* The start and stop ARFCNs must be in the same band */
+ if (gsm_arfcn2band_rc(req->band_arfcn_start, &band_start) != 0 ||
+ gsm_arfcn2band_rc(req->band_arfcn_stop, &band_stop) != 0 ||
+ band_start != band_stop) {
+ LOGPFSML(fi, LOGL_ERROR, "Full power scan request has invalid params\n");
+ return;
+ }
+
+ trxcon->fi_data = talloc_memdup(fi, req, sizeof(*req));
+ OSMO_ASSERT(trxcon->fi_data != NULL);
+
+ /* trxcon_st_full_power_scan_onenter() sends the initial TRXCON_PHYIF_CMDT_MEASURE */
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */
+}
+
+static void trxcon_st_reset_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_FBSB_SEARCH_REQ:
+ {
+ const struct trxcon_param_fbsb_search_req *req = data;
+ unsigned long timeout_fns, timeout_ms;
+
+ /* Some PHYs need additional time to tune (in TDMA FNs) */
+ timeout_fns = req->timeout_fns + trxcon->phy_quirks.fbsb_extend_fns;
+ timeout_ms = timeout_fns * GSM_TDMA_FN_DURATION_uS / 1000;
+ osmo_fsm_inst_state_chg_ms(fi, TRXCON_ST_FBSB_SEARCH, timeout_ms, 0);
+
+ l1sched_configure_ts(trxcon->sched, 0, req->pchan_config);
+
+ /* Only if current ARFCN differs */
+ if (trxcon->l1p.band_arfcn != req->band_arfcn) {
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_SETFREQ_H0,
+ .param.setfreq_h0 = {
+ .band_arfcn = req->band_arfcn,
+ },
+ };
+
+ /* Update current ARFCN */
+ trxcon->l1p.band_arfcn = req->band_arfcn;
+
+ /* Tune transceiver to required ARFCN */
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ }
+
+ const struct trxcon_phyif_cmd phycmd = { .type = TRXCON_PHYIF_CMDT_POWERON };
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ break;
+ }
+ case TRXCON_EV_FULL_POWER_SCAN_REQ:
+ handle_full_power_scan_req(fi, (const struct trxcon_param_full_power_scan_req *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_st_full_power_scan_onenter(struct osmo_fsm_inst *fi,
+ uint32_t prev_state)
+{
+ const struct trxcon_inst *trxcon = fi->priv;
+ const struct trxcon_param_full_power_scan_req *req = trxcon->fi_data;
+
+ /* req->band_arfcn_start holds the current ARFCN */
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_MEASURE,
+ .param.measure = {
+ .band_arfcn = req->band_arfcn_start,
+ },
+ };
+
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+}
+
+static void trxcon_st_full_power_scan_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_FULL_POWER_SCAN_RES:
+ {
+ struct trxcon_param_full_power_scan_req *req = trxcon->fi_data;
+ const struct trxcon_param_full_power_scan_res *res = data;
+
+ if (req == NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx unexpected power scan result\n");
+ break;
+ }
+
+ /* req->band_arfcn_start holds the expected ARFCN */
+ if (res->band_arfcn != req->band_arfcn_start) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx power scan result "
+ "with unexpected ARFCN %u (expected %u)\n",
+ res->band_arfcn & ~ARFCN_FLAG_MASK,
+ req->band_arfcn_start & ~ARFCN_FLAG_MASK);
+ break;
+ }
+
+ if (res->band_arfcn < req->band_arfcn_stop) {
+ l1ctl_tx_pm_conf(trxcon, res->band_arfcn, res->dbm, false);
+ /* trxcon_st_full_power_scan_onenter() sends the next TRXCON_PHYIF_CMDT_MEASURE */
+ req->band_arfcn_start = res->band_arfcn + 1;
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */
+ } else {
+ l1ctl_tx_pm_conf(trxcon, res->band_arfcn, res->dbm, true);
+ LOGPFSML(fi, LOGL_INFO, "Full power scan completed\n");
+ TALLOC_FREE(trxcon->fi_data);
+ }
+ break;
+ }
+ case TRXCON_EV_FULL_POWER_SCAN_REQ:
+ handle_full_power_scan_req(fi, (const struct trxcon_param_full_power_scan_req *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_st_fbsb_search_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_FBSB_SEARCH_RES:
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_BCCH_CCCH, 0, 0);
+ l1ctl_tx_fbsb_conf(trxcon, trxcon->l1p.band_arfcn, trxcon->sched->bsic);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_tx_access_burst_req(struct osmo_fsm_inst *fi,
+ const struct trxcon_param_tx_access_burst_req *req)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->rach_req = (struct l1sched_prim_rach) {
+ .chdr = {
+ .chan_nr = req->chan_nr,
+ .link_id = req->link_id,
+ },
+ .synch_seq = req->synch_seq,
+ .offset = req->offset,
+ .is_11bit = req->is_11bit,
+ .ra = req->ra,
+ };
+
+ l1sched_prim_from_user(trxcon->sched, msg);
+}
+
+static void handle_dch_est_req(struct osmo_fsm_inst *fi,
+ const struct trxcon_param_dch_est_req *req)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ enum gsm_phys_chan_config config;
+ struct l1sched_ts *ts;
+ int rc;
+
+ config = l1sched_chan_nr2pchan_config(req->chan_nr);
+ if (config == GSM_PCHAN_NONE) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to determine channel config\n");
+ return;
+ }
+
+ if (req->hopping) {
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_SETFREQ_H1,
+ .param.setfreq_h1 = {
+ .hsn = req->h1.hsn,
+ .maio = req->h1.maio,
+ .ma = &req->h1.ma[0],
+ .ma_len = req->h1.n,
+ },
+ };
+
+ /* Apply the freq. hopping parameters */
+ if (trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd) != 0)
+ return;
+
+ /* Set current ARFCN to an invalid value */
+ trxcon->l1p.band_arfcn = 0xffff;
+ } else {
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_SETFREQ_H0,
+ .param.setfreq_h0 = {
+ .band_arfcn = req->h0.band_arfcn,
+ },
+ };
+
+ /* Tune transceiver to required ARFCN */
+ if (trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd) != 0)
+ return;
+
+ /* Update current ARFCN */
+ trxcon->l1p.band_arfcn = req->h0.band_arfcn;
+ }
+
+ /* Remove all active timeslots */
+ l1sched_reset(trxcon->sched, false);
+
+ rc = l1sched_configure_ts(trxcon->sched, req->chan_nr & 0x07, config);
+ if (rc)
+ return;
+ ts = trxcon->sched->ts[req->chan_nr & 0x07];
+ OSMO_ASSERT(ts != NULL);
+
+ l1sched_deactivate_all_lchans(ts);
+
+ /* Activate only requested lchans */
+ rc = l1sched_set_lchans(ts, req->chan_nr, 1, req->tch_mode, req->tsc);
+ if (rc) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to activate requested lchans\n");
+ return;
+ }
+
+ /* Store TSC for subsequent PDCH timeslot activation(s) */
+ trxcon->l1p.tsc = req->tsc;
+
+ if (config == GSM_PCHAN_PDCH)
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_PACKET_DATA, 0, 0);
+ else
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_DEDICATED, 0, 0);
+}
+
+static void trxcon_st_bcch_ccch_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_TX_ACCESS_BURST_REQ:
+ handle_tx_access_burst_req(fi, data);
+ break;
+ case TRXCON_EV_TX_ACCESS_BURST_CNF:
+ l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
+ break;
+ case TRXCON_EV_SET_CCCH_MODE_REQ:
+ {
+ struct trxcon_param_set_ccch_tch_mode_req *req = data;
+ enum gsm_phys_chan_config chan_config = req->mode;
+ struct l1sched_ts *ts = trxcon->sched->ts[0];
+
+ /* Make sure that TS0 is allocated and configured */
+ if (ts == NULL || ts->mf_layout == NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "TS0 is not configured\n");
+ return;
+ }
+
+ /* Do nothing if the current mode matches required */
+ if (ts->mf_layout->chan_config != chan_config)
+ l1sched_configure_ts(trxcon->sched, 0, chan_config);
+ req->applied = true;
+ break;
+ }
+ case TRXCON_EV_DCH_EST_REQ:
+ handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data);
+ break;
+ case TRXCON_EV_RX_DATA_IND:
+ l1ctl_tx_dt_ind(trxcon, (const struct trxcon_param_rx_data_ind *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_TX_ACCESS_BURST_REQ:
+ handle_tx_access_burst_req(fi, data);
+ break;
+ case TRXCON_EV_TX_ACCESS_BURST_CNF:
+ l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
+ break;
+ case TRXCON_EV_DCH_EST_REQ:
+ handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data);
+ break;
+ case TRXCON_EV_DCH_REL_REQ:
+ l1sched_reset(trxcon->sched, false);
+ /* TODO: switch to (not implemented) TRXCON_ST_DCH_TUNING? */
+ break;
+ case TRXCON_EV_SET_TCH_MODE_REQ:
+ {
+ struct trxcon_param_set_ccch_tch_mode_req *req = data;
+ unsigned int tn;
+
+ /* Iterate over timeslot list */
+ for (tn = 0; tn < ARRAY_SIZE(trxcon->sched->ts); tn++) {
+ struct l1sched_ts *ts = trxcon->sched->ts[tn];
+ struct l1sched_lchan_state *lchan;
+
+ /* Timeslot is not allocated */
+ if (ts == NULL || ts->mf_layout == NULL)
+ continue;
+
+ /* Iterate over all allocated lchans */
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ /* Omit inactive channels */
+ if (!lchan->active)
+ continue;
+ if (req->mode == GSM48_CMODE_SPEECH_AMR) {
+ int rc = l1sched_lchan_set_amr_cfg(lchan,
+ req->amr.codecs_bitmask,
+ req->amr.start_codec);
+ if (rc)
+ continue;
+ }
+ lchan->tch_mode = req->mode;
+ req->applied = true;
+ }
+ }
+ break;
+ }
+ case TRXCON_EV_CRYPTO_REQ:
+ {
+ const struct trxcon_param_crypto_req *req = data;
+ unsigned int tn = req->chan_nr & 0x07;
+ struct l1sched_ts *ts;
+
+ /* Make sure that required TS is allocated and configured */
+ ts = trxcon->sched->ts[tn];
+ if (ts == NULL || ts->mf_layout == NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "TS%u is not configured\n", tn);
+ return;
+ }
+
+ if (l1sched_start_ciphering(ts, req->a5_algo, req->key, req->key_len) != 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to configure ciphering\n");
+ return;
+ }
+ break;
+ }
+ case TRXCON_EV_TX_DATA_REQ:
+ {
+ const struct trxcon_param_tx_data_req *req = data;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = req->chan_nr,
+ .link_id = req->link_id,
+ .traffic = req->traffic,
+ };
+
+ memcpy(msgb_put(msg, req->data_len), req->data, req->data_len);
+ l1sched_prim_from_user(trxcon->sched, msg);
+ break;
+ }
+ case TRXCON_EV_TX_DATA_CNF:
+ l1ctl_tx_dt_conf(trxcon, (const struct trxcon_param_tx_data_cnf *)data);
+ break;
+ case TRXCON_EV_RX_DATA_IND:
+ l1ctl_tx_dt_ind(trxcon, (const struct trxcon_param_rx_data_ind *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_tbf_cfg_req(struct trxcon_inst *trxcon, uint8_t tn, bool active)
+{
+ struct l1sched_state *sched = trxcon->sched;
+
+ if (active) {
+ struct l1sched_lchan_state *lchan;
+ struct l1sched_ts *ts;
+
+ if (sched->ts[tn] != NULL) /* already enabled */
+ return;
+ if (l1sched_configure_ts(sched, tn, GSM_PCHAN_PDCH) != 0)
+ return;
+ OSMO_ASSERT(sched->ts[tn] != NULL);
+ ts = sched->ts[tn];
+
+ l1sched_activate_lchan(ts, L1SCHED_PDTCH);
+ l1sched_activate_lchan(ts, L1SCHED_PTCCH);
+ llist_for_each_entry(lchan, &ts->lchans, list)
+ lchan->tsc = trxcon->l1p.tsc;
+ } else {
+ l1sched_del_ts(sched, tn);
+ }
+}
+
+static void trxcon_l1gprs_state_changed_cb(struct l1gprs_pdch *pdch, bool active)
+{
+ handle_tbf_cfg_req(pdch->gprs->priv, pdch->tn, active);
+}
+
+static void trxcon_st_packet_data_onenter(struct osmo_fsm_inst *fi,
+ uint32_t prev_state)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ OSMO_ASSERT(trxcon->gprs == NULL);
+ trxcon->gprs = l1gprs_state_alloc(trxcon, trxcon->log_prefix, trxcon);
+ l1gprs_state_set_pdch_changed_cb(trxcon->gprs, trxcon_l1gprs_state_changed_cb);
+ OSMO_ASSERT(trxcon->gprs != NULL);
+}
+
+static void trxcon_st_packet_data_onleave(struct osmo_fsm_inst *fi,
+ uint32_t next_state)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ l1gprs_state_free(trxcon->gprs);
+ trxcon->gprs = NULL;
+}
+
+static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_TX_ACCESS_BURST_REQ:
+ handle_tx_access_burst_req(fi, data);
+ break;
+ case TRXCON_EV_TX_ACCESS_BURST_CNF:
+ l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
+ break;
+ case TRXCON_EV_GPRS_UL_TBF_CFG_REQ:
+ l1gprs_handle_ul_tbf_cfg_req(trxcon->gprs, (struct msgb *)data);
+ break;
+ case TRXCON_EV_GPRS_DL_TBF_CFG_REQ:
+ l1gprs_handle_dl_tbf_cfg_req(trxcon->gprs, (struct msgb *)data);
+ break;
+ case TRXCON_EV_GPRS_UL_BLOCK_REQ:
+ {
+ struct l1gprs_prim_ul_block_req block_req;
+ struct l1sched_prim *prim;
+ struct msgb *msg = data;
+
+ if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0)
+ return;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .frame_nr = block_req.hdr.fn,
+ .chan_nr = RSL_CHAN_OSMO_PDCH | block_req.hdr.tn,
+ .link_id = 0x00,
+ };
+
+ memcpy(msgb_put(msg, block_req.data_len), block_req.data, block_req.data_len);
+ l1sched_prim_from_user(trxcon->sched, msg);
+ break;
+ }
+ case TRXCON_EV_TX_DATA_CNF:
+ {
+ const struct trxcon_param_tx_data_cnf *cnf = data;
+ struct msgb *msg;
+
+ msg = l1gprs_handle_ul_block_cnf(trxcon->gprs,
+ cnf->frame_nr, cnf->chan_nr & 0x07,
+ cnf->data, cnf->data_len);
+ if (msg != NULL)
+ trxcon_l1ctl_send(trxcon, msg);
+ break;
+ }
+ case TRXCON_EV_RX_DATA_IND:
+ {
+ const struct trxcon_param_rx_data_ind *ind = data;
+ struct l1gprs_prim_dl_block_ind block_ind;
+ struct msgb *msg;
+ uint8_t usf = 0xff;
+
+ block_ind = (struct l1gprs_prim_dl_block_ind) {
+ .hdr = {
+ .fn = ind->frame_nr,
+ .tn = ind->chan_nr & 0x07,
+ },
+ .meas = {
+ /* .ber10k is set below */
+ .ci_cb = 180, /* 18 dB */
+ .rx_lev = dbm2rxlev(ind->rssi),
+ },
+ .data_len = ind->data_len,
+ .data = ind->data,
+ };
+
+ if (ind->n_bits_total == 0)
+ block_ind.meas.ber10k = 10000;
+ else
+ block_ind.meas.ber10k = 10000 * ind->n_errors / ind->n_bits_total;
+
+ msg = l1gprs_handle_dl_block_ind(trxcon->gprs, &block_ind, &usf);
+ if (msg != NULL)
+ trxcon_l1ctl_send(trxcon, msg);
+ /* Every fn % 13 == 12 we have either a PTCCH or an IDLE slot, thus
+ * every fn % 13 == 8 we add 5 frames, or 4 frames othrwise. The
+ * resulting value is first fn of the next block. */
+ const uint32_t rts_fn = GSM_TDMA_FN_SUM(ind->frame_nr, (ind->frame_nr % 13 == 8) ? 5 : 4);
+ msg = l1gprs_handle_rts_ind(trxcon->gprs, rts_fn, ind->chan_nr & 0x07, usf);
+ if (msg != NULL)
+ trxcon_l1ctl_send(trxcon, msg);
+ break;
+ }
+ case TRXCON_EV_DCH_EST_REQ:
+ handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data);
+ break;
+ case TRXCON_EV_DCH_REL_REQ:
+ l1sched_reset(trxcon->sched, false);
+ /* TODO: switch to (not implemented) TRXCON_ST_DCH_TUNING? */
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_fsm_pre_term_cb(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ if (trxcon == NULL)
+ return;
+
+ /* Shutdown the scheduler */
+ if (trxcon->sched != NULL)
+ l1sched_free(trxcon->sched);
+ /* Clean up GPRS L1 state */
+ l1gprs_state_free(trxcon->gprs);
+
+ /* Close active connections */
+ if (trxcon->l2if != NULL)
+ trxcon_l1ctl_close(trxcon);
+ if (trxcon->phyif != NULL)
+ trxcon_phyif_close(trxcon->phyif);
+
+ talloc_free(trxcon);
+ fi->priv = NULL;
+}
+
+static const struct osmo_fsm_state trxcon_fsm_states[] = {
+ [TRXCON_ST_RESET] = {
+ .name = "RESET",
+ .out_state_mask = S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_FULL_POWER_SCAN),
+ .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_REQ)
+ | S(TRXCON_EV_FULL_POWER_SCAN_REQ),
+ .action = &trxcon_st_reset_action,
+ },
+ [TRXCON_ST_FULL_POWER_SCAN] = {
+ .name = "FULL_POWER_SCAN",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FULL_POWER_SCAN),
+ .in_event_mask = S(TRXCON_EV_FULL_POWER_SCAN_RES)
+ | S(TRXCON_EV_FULL_POWER_SCAN_REQ),
+ .onenter = &trxcon_st_full_power_scan_onenter,
+ .action = &trxcon_st_full_power_scan_action,
+ },
+ [TRXCON_ST_FBSB_SEARCH] = {
+ .name = "FBSB_SEARCH",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_BCCH_CCCH),
+ .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_RES),
+ .action = &trxcon_st_fbsb_search_action,
+ },
+ [TRXCON_ST_BCCH_CCCH] = {
+ .name = "BCCH_CCCH",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_DEDICATED)
+ | S(TRXCON_ST_PACKET_DATA),
+ .in_event_mask = S(TRXCON_EV_RX_DATA_IND)
+ | S(TRXCON_EV_SET_CCCH_MODE_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_CNF)
+ | S(TRXCON_EV_DCH_EST_REQ),
+ .action = &trxcon_st_bcch_ccch_action,
+ },
+ [TRXCON_ST_DEDICATED] = {
+ .name = "DEDICATED",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_DEDICATED)
+ | S(TRXCON_ST_PACKET_DATA),
+ .in_event_mask = S(TRXCON_EV_DCH_REL_REQ)
+ | S(TRXCON_EV_DCH_EST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_CNF)
+ | S(TRXCON_EV_SET_TCH_MODE_REQ)
+ | S(TRXCON_EV_TX_DATA_REQ)
+ | S(TRXCON_EV_TX_DATA_CNF)
+ | S(TRXCON_EV_RX_DATA_IND)
+ | S(TRXCON_EV_CRYPTO_REQ),
+ .action = &trxcon_st_dedicated_action,
+ },
+ [TRXCON_ST_PACKET_DATA] = {
+ .name = "PACKET_DATA",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_DEDICATED)
+ | S(TRXCON_ST_PACKET_DATA),
+ .in_event_mask = S(TRXCON_EV_DCH_REL_REQ)
+ | S(TRXCON_EV_DCH_EST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_CNF)
+ | S(TRXCON_EV_GPRS_UL_TBF_CFG_REQ)
+ | S(TRXCON_EV_GPRS_DL_TBF_CFG_REQ)
+ | S(TRXCON_EV_GPRS_UL_BLOCK_REQ)
+ | S(TRXCON_EV_RX_DATA_IND)
+ | S(TRXCON_EV_TX_DATA_CNF),
+ .onenter = &trxcon_st_packet_data_onenter,
+ .onleave = &trxcon_st_packet_data_onleave,
+ .action = &trxcon_st_packet_data_action,
+ },
+};
+
+static const struct value_string trxcon_fsm_event_names[] = {
+ OSMO_VALUE_STRING(TRXCON_EV_PHYIF_FAILURE),
+ OSMO_VALUE_STRING(TRXCON_EV_L2IF_FAILURE),
+ OSMO_VALUE_STRING(TRXCON_EV_RESET_FULL_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_RESET_SCHED_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_RES),
+ OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_RES),
+ OSMO_VALUE_STRING(TRXCON_EV_SET_CCCH_MODE_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_SET_TCH_MODE_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_SET_PHY_CONFIG_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_CNF),
+ OSMO_VALUE_STRING(TRXCON_EV_UPDATE_SACCH_CACHE_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_DCH_EST_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_DCH_REL_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_CNF),
+ OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND),
+ OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_TBF_CFG_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_GPRS_DL_TBF_CFG_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_BLOCK_REQ),
+ { 0, NULL }
+};
+
+struct osmo_fsm trxcon_fsm_def = {
+ .name = "trxcon",
+ .states = trxcon_fsm_states,
+ .num_states = ARRAY_SIZE(trxcon_fsm_states),
+ .log_subsys = DLGLOBAL,
+ .event_names = trxcon_fsm_event_names,
+ .allstate_event_mask = S(TRXCON_EV_PHYIF_FAILURE)
+ | S(TRXCON_EV_L2IF_FAILURE)
+ | S(TRXCON_EV_RESET_FULL_REQ)
+ | S(TRXCON_EV_RESET_SCHED_REQ)
+ | S(TRXCON_EV_SET_PHY_CONFIG_REQ)
+ | S(TRXCON_EV_UPDATE_SACCH_CACHE_REQ),
+ .allstate_action = &trxcon_allstate_action,
+ .timer_cb = &trxcon_timer_cb,
+ .pre_term = &trxcon_fsm_pre_term_cb,
+};
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
+}
diff --git a/src/host/trxcon/src/trxcon_inst.c b/src/host/trxcon/src/trxcon_inst.c
new file mode 100644
index 00000000..b7ff5810
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_inst.c
@@ -0,0 +1,107 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (C) 2022 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 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.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+#include <osmocom/bb/l1gprs.h>
+
+extern int g_logc_l1c;
+extern int g_logc_l1d;
+
+void trxcon_set_log_cfg(const int *logc, unsigned int logc_num)
+{
+ int schc = DLGLOBAL;
+ int schd = DLGLOBAL;
+
+ for (unsigned int i = 0; i < logc_num; i++) {
+ switch ((enum trxcon_log_cat)i) {
+ case TRXCON_LOGC_FSM:
+ trxcon_fsm_def.log_subsys = logc[i];
+ break;
+ case TRXCON_LOGC_L1C:
+ g_logc_l1c = logc[i];
+ break;
+ case TRXCON_LOGC_L1D:
+ g_logc_l1d = logc[i];
+ break;
+ case TRXCON_LOGC_SCHC:
+ schc = logc[i];
+ break;
+ case TRXCON_LOGC_SCHD:
+ schd = logc[i];
+ break;
+ case TRXCON_LOGC_GPRS:
+ l1gprs_logging_init(logc[i]);
+ break;
+ }
+ }
+
+ l1sched_logging_init(schc, schd);
+}
+
+struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id)
+{
+ struct trxcon_inst *trxcon;
+ struct osmo_fsm_inst *fi;
+
+ fi = osmo_fsm_inst_alloc(&trxcon_fsm_def, ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi != NULL);
+
+ trxcon = talloc_zero(fi, struct trxcon_inst);
+ OSMO_ASSERT(trxcon != NULL);
+
+ fi->priv = trxcon;
+ trxcon->fi = fi;
+
+ osmo_fsm_inst_update_id_f(fi, "%u", id);
+ trxcon->id = id;
+
+ /* Logging context to be used by both l1ctl and l1sched modules */
+ trxcon->log_prefix = talloc_asprintf(trxcon, "%s: ", osmo_fsm_inst_name(fi));
+
+ /* Init scheduler */
+ const struct l1sched_cfg sched_cfg = {
+ .log_prefix = trxcon->log_prefix,
+ };
+
+ trxcon->sched = l1sched_alloc(trxcon, &sched_cfg, trxcon);
+ if (trxcon->sched == NULL) {
+ trxcon_inst_free(trxcon);
+ return NULL;
+ }
+
+ trxcon->phy_quirks.fbsb_extend_fns = 0;
+
+ return trxcon;
+}
+
+void trxcon_inst_free(struct trxcon_inst *trxcon)
+{
+ if (trxcon == NULL || trxcon->fi == NULL)
+ return;
+ osmo_fsm_inst_term(trxcon->fi, OSMO_FSM_TERM_REQUEST, NULL);
+}
diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/src/trxcon_main.c
index 158e27f7..3901e336 100644
--- a/src/host/trxcon/trxcon.c
+++ b/src/host/trxcon/src/trxcon_main.c
@@ -1,7 +1,8 @@
/*
* OsmocomBB <-> SDR connection bridge
*
- * (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -15,10 +16,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>
@@ -28,10 +25,9 @@
#include <getopt.h>
#include <unistd.h>
#include <signal.h>
+#include <errno.h>
#include <time.h>
-#include <arpa/inet.h>
-
#include <osmocom/core/fsm.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@@ -41,19 +37,16 @@
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include "trxcon.h"
-#include "trx_if.h"
-#include "logging.h"
-#include "l1ctl.h"
-#include "l1ctl_link.h"
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/phyif.h>
+#include <osmocom/bb/trxcon/trx_if.h>
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
#define COPYRIGHT \
- "Copyright (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
+ "Copyright (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
+ "Contributions by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\n" \
"License GPLv2+: GNU GPL version 2 or later " \
"<http://gnu.org/licenses/gpl.html>\n" \
"This is free software: you are free to change and redistribute it.\n" \
@@ -65,87 +58,118 @@ static struct {
int quit;
/* L1CTL specific */
- struct l1ctl_link *l1l;
+ unsigned int max_clients;
const char *bind_socket;
/* TRX specific */
- struct trx_instance *trx;
const char *trx_bind_ip;
const char *trx_remote_ip;
uint16_t trx_base_port;
uint32_t trx_fn_advance;
+
+ /* PHY quirk: FBSB timeout extension (in TDMA FNs) */
+ unsigned int phyq_fbsb_extend_fns;
+
+ /* GSMTAP specific */
+ struct gsmtap_inst *gsmtap;
const char *gsmtap_ip;
-} app_data;
+} app_data = {
+ .max_clients = 1, /* only one L1CTL client by default */
+ .bind_socket = "/tmp/osmocom_l2",
+ .trx_remote_ip = "127.0.0.1",
+ .trx_bind_ip = "0.0.0.0",
+ .trx_base_port = 6700,
+ .trx_fn_advance = 0,
+ .phyq_fbsb_extend_fns = 0,
+};
static void *tall_trxcon_ctx = NULL;
-struct gsmtap_inst *gsmtap = NULL;
-struct osmo_fsm_inst *trxcon_fsm;
-static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
+int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
{
- if (event == L1CTL_EVENT_CONNECT)
- osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_MANAGED, 0, 0);
+ return trx_if_handle_phyif_burst_req(phyif, br);
}
-static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
+int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
{
- switch (event) {
- case L1CTL_EVENT_DISCONNECT:
- osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0);
+ return trx_if_handle_phyif_cmd(phyif, cmd);
+}
- if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) {
- /* Reset scheduler and clock counter */
- sched_trx_reset(app_data.trx, true);
+void trxcon_phyif_close(void *phyif)
+{
+ trx_if_close(phyif);
+}
- /* TODO: implement trx_if_reset() */
- trx_if_cmd_poweroff(app_data.trx);
- trx_if_cmd_echo(app_data.trx);
- }
- break;
- case TRX_EVENT_RSP_ERROR:
- case TRX_EVENT_OFFLINE:
- /* TODO: notify L2 & L3 about that */
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "Unhandled event %u\n", event);
+void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
+{
+ /* Avoid use-after-free: both *fi and *trxcon are children of
+ * the L2IF (L1CTL connection), so we need to re-parent *fi
+ * to NULL before calling l1ctl_client_conn_close(). */
+ talloc_steal(NULL, trxcon->fi);
+ l1ctl_client_conn_close(trxcon->l2if);
+}
+
+int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct l1ctl_client *l1c = trxcon->l2if;
+
+ return l1ctl_client_send(l1c, msg);
+}
+
+static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
+{
+ struct trxcon_inst *trxcon = l1c->priv;
+
+ return trxcon_l1ctl_receive(trxcon, msg);
+}
+
+static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
+{
+ struct trxcon_inst *trxcon;
+
+ trxcon = trxcon_inst_alloc(l1c, l1c->id);
+ if (trxcon == NULL) {
+ l1ctl_client_conn_close(l1c);
+ return;
+ }
+
+ l1c->log_prefix = talloc_strdup(l1c, trxcon->log_prefix);
+ l1c->priv = trxcon;
+ trxcon->l2if = l1c;
+
+ const struct trx_if_params trxcon_phyif_params = {
+ .local_host = app_data.trx_bind_ip,
+ .remote_host = app_data.trx_remote_ip,
+ .base_port = app_data.trx_base_port,
+ .fn_advance = app_data.trx_fn_advance,
+ .instance = trxcon->id,
+
+ .parent_fi = trxcon->fi,
+ .parent_term_event = TRXCON_EV_PHYIF_FAILURE,
+ .priv = trxcon,
+ };
+
+ /* Init transceiver interface */
+ trxcon->phyif = trx_if_open(&trxcon_phyif_params);
+ if (trxcon->phyif == NULL) {
+ /* TRXCON_EV_PHYIF_FAILURE triggers l1ctl_client_conn_close() */
+ osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_PHYIF_FAILURE, NULL);
+ return;
}
+
+ trxcon->gsmtap = app_data.gsmtap;
+ trxcon->phy_quirks.fbsb_extend_fns = app_data.phyq_fbsb_extend_fns;
}
-static struct osmo_fsm_state trxcon_fsm_states[] = {
- [TRXCON_STATE_IDLE] = {
- .in_event_mask = GEN_MASK(L1CTL_EVENT_CONNECT),
- .out_state_mask = GEN_MASK(TRXCON_STATE_MANAGED),
- .name = "IDLE",
- .action = trxcon_fsm_idle_action,
- },
- [TRXCON_STATE_MANAGED] = {
- .in_event_mask = (
- GEN_MASK(L1CTL_EVENT_DISCONNECT) |
- GEN_MASK(TRX_EVENT_RSP_ERROR) |
- GEN_MASK(TRX_EVENT_OFFLINE)),
- .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE),
- .name = "MANAGED",
- .action = trxcon_fsm_managed_action,
- },
-};
+static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
+{
+ struct trxcon_inst *trxcon = l1c->priv;
-static const struct value_string app_evt_names[] = {
- OSMO_VALUE_STRING(L1CTL_EVENT_CONNECT),
- OSMO_VALUE_STRING(L1CTL_EVENT_DISCONNECT),
- OSMO_VALUE_STRING(TRX_EVENT_OFFLINE),
- OSMO_VALUE_STRING(TRX_EVENT_RSP_ERROR),
- { 0, NULL }
-};
+ if (trxcon == NULL || trxcon->fi == NULL)
+ return;
-static struct osmo_fsm trxcon_fsm_def = {
- .name = "trxcon_app_fsm",
- .states = trxcon_fsm_states,
- .num_states = ARRAY_SIZE(trxcon_fsm_states),
- .log_subsys = DAPP,
- .event_names = app_evt_names,
-};
+ osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
+}
static void print_usage(const char *app)
{
@@ -156,19 +180,22 @@ static void print_help(void)
{
printf(" Some help...\n");
printf(" -h --help this text\n");
- printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT);
+ printf(" -d --debug Change debug flags (e.g. DL1C:DSCH)\n");
printf(" -b --trx-bind TRX bind IP address (default 0.0.0.0)\n");
printf(" -i --trx-remote TRX remote IP address (default 127.0.0.1)\n");
printf(" -p --trx-port Base port of TRX instance (default 6700)\n");
- printf(" -f --trx-advance Uplink burst scheduling advance (default 3)\n");
+ printf(" -f --trx-advance Uplink burst scheduling advance (default 0)\n");
+ printf(" -F --fbsb-extend FBSB timeout extension (in TDMA FNs, default 0)\n");
printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n");
printf(" -g --gsmtap-ip The destination IP used for GSMTAP (disabled by default)\n");
+ printf(" -C --max-clients Maximum number of L1CTL connections (default 1)\n");
printf(" -D --daemonize Run as daemon\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
+ char *endptr = NULL;
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
@@ -181,16 +208,20 @@ static void handle_options(int argc, char **argv)
{"trx-remote", 1, 0, 'i'},
{"trx-port", 1, 0, 'p'},
{"trx-advance", 1, 0, 'f'},
+ {"fbsb-extend", 1, 0, 'F'},
{"gsmtap-ip", 1, 0, 'g'},
+ {"max-clients", 1, 0, 'C'},
{"daemonize", 0, 0, 'D'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, "d:b:i:p:f:s:g:Dh",
+ c = getopt_long(argc, argv, "d:b:i:p:f:F:s:g:C:Dh",
long_options, &option_index);
if (c == -1)
break;
+ errno = 0;
+
switch (c) {
case 'h':
print_usage(argv[0]);
@@ -207,10 +238,25 @@ static void handle_options(int argc, char **argv)
app_data.trx_remote_ip = optarg;
break;
case 'p':
- app_data.trx_base_port = atoi(optarg);
+ app_data.trx_base_port = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -p/--trx-port=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
break;
case 'f':
- app_data.trx_fn_advance = atoi(optarg);
+ app_data.trx_fn_advance = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -f/--trx-advance=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'F':
+ app_data.phyq_fbsb_extend_fns = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -F/--fbsb-extend=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
break;
case 's':
app_data.bind_socket = optarg;
@@ -218,6 +264,13 @@ static void handle_options(int argc, char **argv)
case 'g':
app_data.gsmtap_ip = optarg;
break;
+ case 'C':
+ app_data.max_clients = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -C/--max-clients=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
case 'D':
app_data.daemonize = 1;
break;
@@ -227,26 +280,13 @@ static void handle_options(int argc, char **argv)
}
}
-static void init_defaults(void)
-{
- app_data.bind_socket = "/tmp/osmocom_l2";
- app_data.trx_remote_ip = "127.0.0.1";
- app_data.trx_bind_ip = "0.0.0.0";
- app_data.trx_base_port = 6700;
- app_data.trx_fn_advance = 3;
-
- app_data.debug_mask = NULL;
- app_data.gsmtap_ip = NULL;
- app_data.daemonize = 0;
- app_data.quit = 0;
-}
-
static void signal_handler(int signum)
{
fprintf(stderr, "signal %u received\n", signum);
switch (signum) {
case SIGINT:
+ case SIGTERM:
app_data.quit++;
break;
case SIGABRT:
@@ -271,10 +311,11 @@ static void signal_handler(int signum)
int main(int argc, char **argv)
{
+ struct l1ctl_server_cfg server_cfg;
+ struct l1ctl_server *server = NULL;
int rc = 0;
printf("%s", COPYRIGHT);
- init_defaults();
handle_options(argc, argv);
/* Track the use of talloc NULL memory contexts */
@@ -286,13 +327,14 @@ int main(int argc, char **argv)
/* Setup signal handlers */
signal(SIGINT, &signal_handler);
+ signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
/* Init logging system */
- trx_log_init(tall_trxcon_ctx, app_data.debug_mask);
+ trxcon_logging_init(tall_trxcon_ctx, app_data.debug_mask);
/* Configure pretty logging */
log_set_print_extended_timestamp(osmo_stderr_target, 1);
@@ -300,43 +342,48 @@ int main(int argc, char **argv)
log_set_print_category(osmo_stderr_target, 1);
log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
+
+ osmo_fsm_log_timeouts(true);
+
/* Optional GSMTAP */
if (app_data.gsmtap_ip != NULL) {
- gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1);
- if (!gsmtap) {
- LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP\n");
+ struct log_target *lt;
+
+ app_data.gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1);
+ if (!app_data.gsmtap) {
+ LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP Um logging\n");
goto exit;
}
- /* Suppress ICMP "destination unreachable" errors */
- gsmtap_source_add_sink(gsmtap);
- }
- /* Allocate the application state machine */
- OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
- trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trxcon_ctx,
- NULL, LOGL_DEBUG, "main");
-
- /* Init L1CTL server */
- app_data.l1l = l1ctl_link_init(tall_trxcon_ctx,
- app_data.bind_socket);
- if (app_data.l1l == NULL)
- goto exit;
-
- /* Init transceiver interface */
- app_data.trx = trx_if_open(tall_trxcon_ctx,
- app_data.trx_bind_ip, app_data.trx_remote_ip,
- app_data.trx_base_port);
- if (!app_data.trx)
- goto exit;
+ lt = log_target_create_gsmtap(app_data.gsmtap_ip, GSMTAP_UDP_PORT,
+ "trxcon", false, false);
+ if (lt == NULL) {
+ LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP logging target\n");
+ goto exit;
+ } else {
+ log_add_target(lt);
+ }
- /* Bind L1CTL with TRX and vice versa */
- app_data.l1l->trx = app_data.trx;
- app_data.trx->l1l = app_data.l1l;
+ /* Suppress ICMP "destination unreachable" errors */
+ gsmtap_source_add_sink(app_data.gsmtap);
+ }
- /* Init scheduler */
- rc = sched_trx_init(app_data.trx, app_data.trx_fn_advance);
- if (rc)
+ /* Start the L1CTL server */
+ server_cfg = (struct l1ctl_server_cfg) {
+ .sock_path = app_data.bind_socket,
+ .num_clients_max = app_data.max_clients,
+ .conn_read_cb = &l1ctl_rx_cb,
+ .conn_accept_cb = &l1ctl_conn_accept_cb,
+ .conn_close_cb = &l1ctl_conn_close_cb,
+ };
+
+ server = l1ctl_server_alloc(tall_trxcon_ctx, &server_cfg);
+ if (server == NULL) {
+ rc = EXIT_FAILURE;
goto exit;
+ }
LOGP(DAPP, LOGL_NOTICE, "Init complete\n");
@@ -355,13 +402,8 @@ int main(int argc, char **argv)
osmo_select_main(0);
exit:
- /* Close active connections */
- l1ctl_link_shutdown(app_data.l1l);
- sched_trx_shutdown(app_data.trx);
- trx_if_close(app_data.trx);
-
- /* Shutdown main state machine */
- osmo_fsm_inst_free(trxcon_fsm);
+ if (server != NULL)
+ l1ctl_server_free(server);
/* Deinitialize logging */
log_fini();
diff --git a/src/host/trxcon/src/trxcon_shim.c b/src/host/trxcon/src/trxcon_shim.c
new file mode 100644
index 00000000..ed2d402e
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_shim.c
@@ -0,0 +1,262 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (C) 2022-2023 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 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.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/phyif.h>
+#include <osmocom/bb/l1sched/l1sched.h>
+
+static void trxcon_gsmtap_send(struct trxcon_inst *trxcon,
+ const struct l1sched_prim_chdr *chdr,
+ const uint8_t *data, size_t data_len,
+ int8_t signal_dbm, uint8_t snr, bool uplink)
+{
+ uint16_t band_arfcn = trxcon->l1p.band_arfcn;
+ uint8_t chan_type, ss, tn;
+
+ if (uplink)
+ band_arfcn |= ARFCN_UPLINK;
+ if (rsl_dec_chan_nr(chdr->chan_nr, &chan_type, &ss, &tn) != 0)
+ return;
+ chan_type = chantype_rsl2gsmtap2(chan_type, chdr->link_id, chdr->traffic);
+
+ gsmtap_send(trxcon->gsmtap, band_arfcn, tn, chan_type, ss,
+ chdr->frame_nr, signal_dbm, snr,
+ data, data_len);
+}
+
+/* External L1 API for the scheduler */
+int l1sched_handle_burst_req(struct l1sched_state *sched,
+ const struct l1sched_burst_req *br)
+{
+ struct trxcon_inst *trxcon = sched->priv;
+ const struct trxcon_phyif_burst_req phybr = {
+ .fn = br->fn,
+ .tn = br->tn,
+ .pwr = br->pwr,
+ .burst = &br->burst[0],
+ .burst_len = br->burst_len,
+ };
+
+ return trxcon_phyif_handle_burst_req(trxcon->phyif, &phybr);
+}
+
+/* External L2 API for the scheduler */
+static int handle_prim_data_ind(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_rx_data_ind ind = {
+ .traffic = prim->data_ind.chdr.traffic,
+ .chan_nr = prim->data_ind.chdr.chan_nr,
+ .link_id = prim->data_ind.chdr.link_id,
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->data_ind.chdr.frame_nr,
+ .toa256 = prim->data_ind.toa256,
+ .rssi = prim->data_ind.rssi,
+ .n_errors = prim->data_ind.n_errors,
+ .n_bits_total = prim->data_ind.n_bits_total,
+ .data_len = msgb_l2len(msg),
+ .data = msgb_l2(msg),
+ };
+
+ if (trxcon->gsmtap != NULL && ind.data_len > 0) {
+ trxcon_gsmtap_send(trxcon, &prim->data_ind.chdr,
+ ind.data, ind.data_len,
+ ind.rssi, 0, false);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
+}
+
+static int handle_prim_data_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_tx_data_cnf cnf = {
+ .traffic = prim->data_cnf.traffic,
+ .chan_nr = prim->data_cnf.chan_nr,
+ .link_id = prim->data_cnf.link_id,
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->data_cnf.frame_nr,
+ .data_len = msgb_l2len(msg),
+ .data = msgb_l2(msg),
+ };
+
+ if (trxcon->gsmtap != NULL) {
+ trxcon_gsmtap_send(trxcon, &prim->data_cnf,
+ msgb_l2(msg), msgb_l2len(msg),
+ 0, 0, true);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
+}
+
+static int handle_prim_rach_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_tx_access_burst_cnf cnf = {
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->rach_cnf.chdr.frame_nr,
+ };
+
+ if (trxcon->gsmtap != NULL) {
+ if (prim->rach_cnf.is_11bit) {
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra >> 3));
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra & 0x07));
+ } else {
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra));
+ }
+
+ trxcon_gsmtap_send(trxcon, &prim->rach_cnf.chdr,
+ msgb_l2(msg), msgb_l2len(msg),
+ 0, 0, true);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
+}
+
+int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_inst *trxcon = sched->priv;
+ int rc = 0;
+
+ LOGPFSML(trxcon->fi, LOGL_DEBUG,
+ "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION):
+ rc = handle_prim_data_ind(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_CONFIRM):
+ rc = handle_prim_data_cnf(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_CONFIRM):
+ rc = handle_prim_rach_cnf(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION):
+ if (trxcon->fi->state == TRXCON_ST_FBSB_SEARCH)
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION):
+ {
+ struct trxcon_param_set_phy_config_req req = {
+ .type = TRXCON_PHY_CFGT_PCHAN_COMB,
+ .pchan_comb = {
+ .tn = prim->pchan_comb_ind.tn,
+ .pchan = prim->pchan_comb_ind.pchan,
+ },
+ };
+
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
+ break;
+ }
+ default:
+ LOGPFSML(trxcon->fi, LOGL_ERROR,
+ "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+ rc = -ENOTSUP;
+ }
+
+ msgb_free(msg);
+ return rc;
+}
+
+/* External L1 API for the PHYIF */
+int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts)
+{
+ struct trxcon_inst *trxcon = priv;
+ struct l1sched_burst_req br = {
+ .fn = rts->fn,
+ .tn = rts->tn,
+ .burst_len = 0, /* NOPE.ind */
+ };
+
+ l1sched_pull_burst(trxcon->sched, &br);
+ return l1sched_handle_burst_req(trxcon->sched, &br);
+}
+
+int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind,
+ struct trxcon_phyif_rtr_rsp *rsp)
+{
+ struct trxcon_inst *trxcon = priv;
+ struct l1sched_probe probe = {
+ .fn = ind->fn,
+ .tn = ind->tn,
+ };
+
+ l1sched_handle_rx_probe(trxcon->sched, &probe);
+
+ memset(rsp, 0x00, sizeof(*rsp));
+
+ if (probe.flags & L1SCHED_PROBE_F_ACTIVE)
+ rsp->flags |= TRXCON_PHYIF_RTR_F_ACTIVE;
+
+ return 0;
+}
+
+int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *phybi)
+{
+ struct trxcon_inst *trxcon = priv;
+ struct l1sched_burst_ind bi = {
+ .fn = phybi->fn,
+ .tn = phybi->tn,
+ .toa256 = phybi->toa256,
+ .rssi = phybi->rssi,
+ /* .burst[] is populated below */
+ .burst_len = phybi->burst_len,
+ };
+
+ OSMO_ASSERT(phybi->burst_len <= sizeof(bi.burst));
+ memcpy(&bi.burst[0], phybi->burst, phybi->burst_len);
+
+ /* Poke scheduler */
+ return l1sched_handle_rx_burst(trxcon->sched, &bi);
+}
+
+int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp)
+{
+ struct trxcon_inst *trxcon = priv;
+
+ switch (rsp->type) {
+ case TRXCON_PHYIF_CMDT_MEASURE:
+ {
+ const struct trxcon_phyif_rspp_measure *meas = &rsp->param.measure;
+ struct trxcon_param_full_power_scan_res res = {
+ .band_arfcn = meas->band_arfcn,
+ .dbm = meas->dbm,
+ };
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res);
+ }
+ default:
+ LOGPFSML(trxcon->fi, LOGL_ERROR,
+ "Unhandled PHYIF response (type 0x%02x)\n", rsp->type);
+ return -ENODEV;
+ }
+}
diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h
deleted file mode 100644
index fa66d4a7..00000000
--- a/src/host/trxcon/trx_if.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/fsm.h>
-
-#include "scheduler.h"
-#include "sched_trx.h"
-
-#define TRXC_BUF_SIZE 1024
-#define TRXD_BUF_SIZE 512
-
-/* Forward declaration to avoid mutual include */
-struct l1ctl_link;
-
-enum trx_fsm_states {
- TRX_STATE_OFFLINE = 0,
- TRX_STATE_IDLE,
- TRX_STATE_ACTIVE,
- TRX_STATE_RSP_WAIT,
-};
-
-struct trx_instance {
- struct osmo_fd trx_ofd_ctrl;
- struct osmo_fd trx_ofd_data;
-
- struct osmo_timer_list trx_ctrl_timer;
- struct llist_head trx_ctrl_list;
- struct osmo_fsm_inst *fsm;
-
- /* HACK: we need proper state machines */
- uint32_t prev_state;
- bool powered_up;
-
- /* GSM L1 specific */
- uint16_t pm_band_arfcn_start;
- uint16_t pm_band_arfcn_stop;
- uint16_t band_arfcn;
- uint8_t tx_power;
- uint8_t bsic;
- uint8_t tsc;
- int8_t ta;
-
- /* Scheduler stuff */
- struct trx_sched sched;
- struct trx_ts *ts_list[TRX_TS_COUNT];
-
- /* Bind L1CTL link */
- struct l1ctl_link *l1l;
-};
-
-struct trx_ctrl_msg {
- struct llist_head list;
- char cmd[TRXC_BUF_SIZE];
- int retry_cnt;
- int critical;
- int cmd_len;
-};
-
-struct trx_instance *trx_if_open(void *tall_ctx,
- const char *local_host, const char *remote_host, uint16_t port);
-void trx_if_flush_ctrl(struct trx_instance *trx);
-void trx_if_close(struct trx_instance *trx);
-
-int trx_if_cmd_poweron(struct trx_instance *trx);
-int trx_if_cmd_poweroff(struct trx_instance *trx);
-int trx_if_cmd_echo(struct trx_instance *trx);
-
-int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta);
-
-int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn);
-int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn);
-
-int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type);
-int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn,
- uint8_t maio, uint16_t *ma, size_t ma_len);
-
-int trx_if_cmd_measure(struct trx_instance *trx,
- uint16_t band_arfcn_start, uint16_t band_arfcn_stop);
-
-int trx_if_tx_burst(struct trx_instance *trx,
- const struct sched_burst_req *br);
diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h
deleted file mode 100644
index 9a0792bf..00000000
--- a/src/host/trxcon/trxcon.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#define GEN_MASK(state) (0x01 << state)
-
-extern struct osmo_fsm_inst *trxcon_fsm;
-extern struct gsmtap_inst *gsmtap;
-
-enum trxcon_fsm_states {
- TRXCON_STATE_IDLE = 0,
- TRXCON_STATE_MANAGED,
-};
-
-enum trxcon_fsm_events {
- /* L1CTL specific events */
- L1CTL_EVENT_CONNECT,
- L1CTL_EVENT_DISCONNECT,
-
- /* TRX specific events */
- TRX_EVENT_RSP_ERROR,
- TRX_EVENT_OFFLINE,
-};
diff --git a/src/host/virt_phy/configure.ac b/src/host/virt_phy/configure.ac
index fbff2c11..19e4bc7a 100644
--- a/src/host/virt_phy/configure.ac
+++ b/src/host/virt_phy/configure.ac
@@ -3,6 +3,8 @@ AC_INIT([virtphy], 0.0.0)
AM_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE([foreign dist-bzip2 subdir-objects])
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -33,9 +35,34 @@ then
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
+AC_ARG_ENABLE(werror,
+ [AS_HELP_STRING(
+ [--enable-werror],
+ [Turn all compiler warnings into errors, with exceptions:
+ a) deprecation (allow upstream to mark deprecation without breaking builds);
+ b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
+ ]
+ )],
+ [werror=$enableval], [werror="no"])
+if test x"$werror" = x"yes"
+then
+ WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
+ WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
+ WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
+ CFLAGS="$CFLAGS $WERROR_FLAGS"
+ CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
+fi
+
+AC_MSG_RESULT([CFLAGS="$CFLAGS"])
+AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
+
AC_CONFIG_FILES([
Makefile
include/Makefile
+ include/osmocom/Makefile
+ include/osmocom/bb/Makefile
+ include/osmocom/bb/virtphy/Makefile
src/Makefile
])
AC_OUTPUT
diff --git a/src/host/virt_phy/include/Makefile.am b/src/host/virt_phy/include/Makefile.am
index 6048a4b3..9d963a02 100644
--- a/src/host/virt_phy/include/Makefile.am
+++ b/src/host/virt_phy/include/Makefile.am
@@ -1,10 +1,3 @@
-noinst_HEADERS = \
- virtphy/logging.h \
- virtphy/osmo_mcast_sock.h \
- virtphy/l1ctl_sock.h \
- virtphy/virtual_um.h \
- virtphy/gsmtapl1_if.h \
- virtphy/virt_l1_sched.h \
- virtphy/common_util.h \
- virtphy/l1ctl_sap.h \
- virtphy/virt_l1_model.h
+SUBDIRS = \
+ osmocom \
+ $(NULL)
diff --git a/src/host/virt_phy/include/osmocom/Makefile.am b/src/host/virt_phy/include/osmocom/Makefile.am
new file mode 100644
index 00000000..83c6385c
--- /dev/null
+++ b/src/host/virt_phy/include/osmocom/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ bb \
+ $(NULL)
diff --git a/src/host/virt_phy/include/osmocom/bb/Makefile.am b/src/host/virt_phy/include/osmocom/bb/Makefile.am
new file mode 100644
index 00000000..9a4b939a
--- /dev/null
+++ b/src/host/virt_phy/include/osmocom/bb/Makefile.am
@@ -0,0 +1,8 @@
+SUBDIRS = \
+ virtphy \
+ $(NULL)
+
+noinst_HEADERS = \
+ l1ctl_proto.h \
+ l1gprs.h \
+ $(NULL)
diff --git a/src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h b/src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h
new file mode 120000
index 00000000..ee19b80e
--- /dev/null
+++ b/src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h
@@ -0,0 +1 @@
+../../../../../../include/l1ctl_proto.h \ No newline at end of file
diff --git a/src/host/virt_phy/include/osmocom/bb/l1gprs.h b/src/host/virt_phy/include/osmocom/bb/l1gprs.h
new file mode 120000
index 00000000..3bf85176
--- /dev/null
+++ b/src/host/virt_phy/include/osmocom/bb/l1gprs.h
@@ -0,0 +1 @@
+../../../../../../include/l1gprs.h \ No newline at end of file
diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am b/src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am
new file mode 100644
index 00000000..75846096
--- /dev/null
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am
@@ -0,0 +1,11 @@
+noinst_HEADERS = \
+ logging.h \
+ osmo_mcast_sock.h \
+ l1ctl_sock.h \
+ virtual_um.h \
+ gsmtapl1_if.h \
+ virt_l1_sched.h \
+ common_util.h \
+ l1ctl_sap.h \
+ virt_l1_model.h \
+ $(NULL)
diff --git a/src/host/virt_phy/include/virtphy/common_util.h b/src/host/virt_phy/include/osmocom/bb/virtphy/common_util.h
index 2585d069..2585d069 100644
--- a/src/host/virt_phy/include/virtphy/common_util.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/common_util.h
diff --git a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h b/src/host/virt_phy/include/osmocom/bb/virtphy/gsmtapl1_if.h
index 8dab6b28..c9e92525 100644
--- a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/gsmtapl1_if.h
@@ -2,9 +2,10 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/gsmtap.h>
-#include <virtphy/virtual_um.h>
-#include <virtphy/l1ctl_sock.h>
-#include <virtphy/virt_l1_model.h>
+
+#include <osmocom/bb/virtphy/virtual_um.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
void gsmtapl1_init(struct l1_model_ms *model);
void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h
new file mode 100644
index 00000000..e756c140
--- /dev/null
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/bb/virtphy/virtual_um.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/l1ctl_proto.h>
+
+/* following sizes are used for message allocation */
+/* size of layer 3 header */
+#define L3_MSG_HEAD 4
+/* size of layer 3 payload */
+#define L3_MSG_DATA 200
+#define L3_MSG_SIZE (sizeof(struct l1ctl_hdr) + L3_MSG_HEAD + L3_MSG_DATA)
+
+/* lchan link ID */
+#define LID_SACCH 0x40
+#define LID_DEDIC 0x00
+
+/* signature strengths for the ms */
+#define MIN_SIG_LEV_DBM -110
+#define MAX_SIG_LEV_DBM -63
+
+/* Ignore all flags of the arfcn */
+#define ARFCN_NO_FLAGS_MASK 0x0fff
+
+
+void l1ctl_sap_init(struct l1_model_ms *model);
+void l1ctl_sap_exit(struct l1_model_ms *model);
+void prim_pm_init(struct l1_model_ms *model);
+void prim_pm_exit(struct l1_model_ms *model);
+void l1ctl_sap_tx_to_l23_inst(struct l1_model_ms *model, struct msgb *msg);
+void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_client *lsc, struct msgb *msg);
+void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg);
+
+/* utility methods */
+struct msgb *l1ctl_msgb_alloc(uint8_t msg_type);
+struct msgb *l1ctl_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr,
+ uint16_t arfcn);
+
+/* receive routines */
+void l1ctl_rx_fbsb_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_param_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_dm_freq_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_crypto_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_rach_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_data_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_pm_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_ccch_mode_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg);
+void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg);
+
+/* transmit routines */
+void l1ctl_tx_reset(struct l1_model_ms *ms, uint8_t msg_type, uint8_t reset_type);
+void l1ctl_tx_rach_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t arfcn);
+void l1ctl_tx_data_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint16_t arfcn);
+void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
+ uint8_t chan_nr, uint32_t fn, uint8_t snr,
+ uint8_t rxlev, uint8_t num_biterr,
+ uint8_t fire_crc);
+void l1ctl_tx_traffic_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint16_t arfcn);
+void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
+ uint8_t chan_nr, uint32_t fn, uint8_t snr,
+ uint8_t rxlev, uint8_t num_biterr,
+ uint8_t fire_crc);
+void l1ctl_tx_pm_conf(struct l1_model_ms *ms, struct l1ctl_pm_req *pm_req);
+void l1ctl_tx_fbsb_conf(struct l1_model_ms *ms, uint8_t res, uint16_t arfcn);
+void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *ms, uint8_t ccch_mode);
+void l1ctl_tx_tch_mode_conf(struct l1_model_ms *ms, uint8_t tch_mode, uint8_t audio_mode);
+void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg,
+ uint32_t fn, uint8_t tn, uint8_t rxlev);
+
+/* scheduler functions */
+uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr,
+ uint8_t link_id);
diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sock.h b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sock.h
index 2c98fa58..2c98fa58 100644
--- a/src/host/virt_phy/include/virtphy/l1ctl_sock.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sock.h
diff --git a/src/host/virt_phy/include/virtphy/logging.h b/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h
index b22db992..1727453e 100644
--- a/src/host/virt_phy/include/virtphy/logging.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h
@@ -7,6 +7,7 @@ enum virtphy_log_cat {
DL1C,
DL1P,
DVIRPHY,
+ DGPRS,
DMAIN
};
@@ -15,5 +16,5 @@ enum virtphy_log_cat {
extern const struct log_info ms_log_info;
-int ms_log_init(char *cat_mask);
+int ms_log_init(void *ctx, const char *cat_mask);
const char *getL1ctlPrimName(uint8_t type);
diff --git a/src/host/virt_phy/include/virtphy/osmo_mcast_sock.h b/src/host/virt_phy/include/osmocom/bb/virtphy/osmo_mcast_sock.h
index aa2013c6..aa2013c6 100644
--- a/src/host/virt_phy/include/virtphy/osmo_mcast_sock.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/osmo_mcast_sock.h
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_model.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h
index ffed7082..94581f61 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_model.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h
@@ -2,11 +2,12 @@
/* Per-MS specific state, closely attached to the L1CTL user progran */
-#include <virtphy/virtual_um.h>
-#include <virtphy/l1ctl_sock.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/timer.h>
+#include <osmocom/bb/virtphy/virtual_um.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+
#define L1S_NUM_NEIGH_CELL 6
#define A5_KEY_LEN 8
@@ -15,7 +16,6 @@ enum ms_state {
MS_STATE_IDLE_SYNCING,
MS_STATE_IDLE_CAMPING,
MS_STATE_DEDICATED,
- MS_STATE_TBF
};
@@ -76,15 +76,6 @@ struct l1_state_ms {
uint8_t tsc; // training sequence code (unused in virtual um)
uint8_t h; // hopping enabled flag (unused in virtual um)
} dedicated;
- struct {
- struct {
- uint8_t usf[8];
- struct llist_head tx_queue;
- } ul;
- struct {
- uint8_t tfi[8];
- } dl;
- } tbf;
/* fbsb state */
struct {
@@ -115,6 +106,8 @@ struct l1_model_ms {
struct l1ctl_sock_client *lsc;
/* pointer to the (shared) GSMTAP/VirtUM socket to talk to BTS(s) */
struct virt_um_inst *vui;
+ /* GPRS state (MAC layer) */
+ struct l1gprs_state *gprs;
/* actual per-MS state */
struct l1_state_ms state;
};
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_sched.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_sched.h
index b8d401ff..d5a1630f 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_sched.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_sched.h
@@ -1,9 +1,11 @@
#pragma once
+
#include <osmocom/core/msgb.h>
-#include <virtphy/virt_l1_model.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsm_utils.h>
-#include <virtphy/virt_l1_sched.h>
+
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
struct l1_model_ms;
diff --git a/src/host/virt_phy/include/virtphy/virtual_um.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virtual_um.h
index fe060929..fe060929 100644
--- a/src/host/virt_phy/include/virtphy/virtual_um.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virtual_um.h
diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
deleted file mode 100644
index d3562a17..00000000
--- a/src/host/virt_phy/include/virtphy/l1ctl_sap.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <osmocom/core/msgb.h>
-#include <l1ctl_proto.h>
-#include <virtphy/virtual_um.h>
-#include <virtphy/l1ctl_sock.h>
-#include <virtphy/virt_l1_model.h>
-
-/* following sizes are used for message allocation */
-/* size of layer 3 header */
-#define L3_MSG_HEAD 4
-/* size of layer 3 payload */
-#define L3_MSG_DATA 200
-#define L3_MSG_SIZE (sizeof(struct l1ctl_hdr) + L3_MSG_HEAD + L3_MSG_DATA)
-
-/* lchan link ID */
-#define LID_SACCH 0x40
-#define LID_DEDIC 0x00
-
-/* signature strengths for the ms */
-#define MIN_SIG_LEV_DBM -110
-#define MAX_SIG_LEV_DBM -63
-
-/* Ignore all flags of the arfcn */
-#define ARFCN_NO_FLAGS_MASK 0x0fff
-
-
-void l1ctl_sap_init(struct l1_model_ms *model);
-void l1ctl_sap_exit(struct l1_model_ms *model);
-void prim_pm_init(struct l1_model_ms *model);
-void prim_pm_exit(struct l1_model_ms *model);
-void l1ctl_sap_tx_to_l23_inst(struct l1_model_ms *model, struct msgb *msg);
-void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_client *lsc, struct msgb *msg);
-void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg);
-
-/* utility methods */
-struct msgb *l1ctl_msgb_alloc(uint8_t msg_type);
-struct msgb *l1ctl_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr,
- uint16_t arfcn);
-
-/* receive routines */
-void l1ctl_rx_fbsb_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_dm_est_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_dm_rel_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_param_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_dm_freq_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_crypto_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_rach_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_data_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_pm_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_reset_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_ccch_mode_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_tch_mode_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_neigh_pm_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_traffic_req(struct l1_model_ms *, struct msgb *msg);
-void l1ctl_rx_sim_req(struct l1_model_ms *, struct msgb *msg);
-
-/* transmit routines */
-void l1ctl_tx_reset(struct l1_model_ms *, uint8_t msg_type, uint8_t reset_type);
-void l1ctl_tx_rach_conf(struct l1_model_ms *, uint32_t fn, uint16_t arfcn);
-void l1ctl_tx_data_conf(struct l1_model_ms *, uint32_t fn, uint16_t snr, uint16_t arfcn);
-void l1ctl_tx_data_ind(struct l1_model_ms *, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
- uint8_t chan_nr, uint32_t fn, uint8_t snr,
- uint8_t signal_dbm, uint8_t num_biterr,
- uint8_t fire_crc);
-void l1ctl_tx_traffic_conf(struct l1_model_ms *, uint32_t fn, uint16_t snr, uint16_t arfcn);
-void l1ctl_tx_traffic_ind(struct l1_model_ms *, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
- uint8_t chan_nr, uint32_t fn, uint8_t snr,
- uint8_t signal_dbm, uint8_t num_biterr,
- uint8_t fire_crc);
-void l1ctl_tx_pm_conf(struct l1_model_ms *, struct l1ctl_pm_req *pm_req);
-void l1ctl_tx_fbsb_conf(struct l1_model_ms *, uint8_t res, uint16_t arfcn);
-void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *, uint8_t ccch_mode);
-void l1ctl_tx_tch_mode_conf(struct l1_model_ms *, uint8_t tch_mode, uint8_t audio_mode);
-
-/* scheduler functions */
-uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr,
- uint8_t link_id);
diff --git a/src/host/virt_phy/src/Makefile.am b/src/host/virt_phy/src/Makefile.am
index bfd0dfaa..f10639f5 100644
--- a/src/host/virt_phy/src/Makefile.am
+++ b/src/host/virt_phy/src/Makefile.am
@@ -1,11 +1,28 @@
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS)
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/../layer23/include
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
bin_PROGRAMS = virtphy
-virtphy_SOURCES = virtphy.c l1ctl_sock.c gsmtapl1_if.c l1ctl_sap.c virt_prim_pm.c virt_prim_fbsb.c virt_prim_rach.c virt_prim_data.c virt_prim_traffic.c virt_l1_sched_simple.c logging.c virt_l1_model.c shared/virtual_um.c shared/osmo_mcast_sock.c
-virtphy_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
-virtphy_LDFLAGS = -pthread
-# debug output
-all:
- $(info $$AM_CPPFLAGS is [${AM_CPPFLAGS}])
+virtphy_SOURCES = \
+ virtphy.c \
+ l1gprs.c \
+ logging.c \
+ gsmtapl1_if.c \
+ l1ctl_sock.c \
+ l1ctl_sap.c \
+ virt_prim_pm.c \
+ virt_prim_fbsb.c \
+ virt_prim_rach.c \
+ virt_prim_data.c \
+ virt_prim_pdch.c \
+ virt_prim_traffic.c \
+ virt_l1_sched_simple.c \
+ virt_l1_model.c \
+ shared/virtual_um.c \
+ shared/osmo_mcast_sock.c \
+ $(NULL)
+
+virtphy_LDADD = \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/src/host/virt_phy/src/gsmtapl1_if.c b/src/host/virt_phy/src/gsmtapl1_if.c
index 83b01fec..ef8d6840 100644
--- a/src/host/virt_phy/src/gsmtapl1_if.c
+++ b/src/host/virt_phy/src/gsmtapl1_if.c
@@ -20,6 +20,11 @@
*
*/
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
@@ -28,18 +33,15 @@
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/core/msgb.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <l1ctl_proto.h>
-#include <virtphy/virtual_um.h>
-#include <virtphy/l1ctl_sock.h>
-#include <virtphy/virt_l1_model.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/gsmtapl1_if.h>
-#include <virtphy/logging.h>
-#include <virtphy/virt_l1_sched.h>
+
+#include <osmocom/bb/virtphy/virtual_um.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/l1ctl_proto.h>
static char *pseudo_lchan_name(uint16_t arfcn, uint8_t ts, uint8_t ss, uint8_t sub_type)
{
@@ -82,7 +84,7 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn
struct gsmtap_hdr *gh;
struct msgb *outmsg; /* msg to send with gsmtap header prepended */
uint16_t arfcn;
- uint8_t signal_dbm = 63; /* signal strength */
+ uint8_t signal_dbm = rxlev2dbm(63); /* signal strength */
uint8_t snr = 63; /* signal noise ratio, 63 is best */
uint8_t *data = msgb_l2(msg); /* data to transmit (whole message without l1 header) */
uint8_t data_len = msgb_l2len(msg); /* length of data */
@@ -92,23 +94,16 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn
uint8_t timeslot; /* tdma timeslot to send in (0-7) */
uint8_t gsmtap_chan; /* the gsmtap channel */
- switch (ms->state.state) {
- case MS_STATE_DEDICATED:
- case MS_STATE_TBF:
+ if (ms->state.state == MS_STATE_DEDICATED)
arfcn = ms->state.dedicated.band_arfcn;
- break;
- default:
+ else
arfcn = ms->state.serving_cell.arfcn;
- break;
- }
switch (l1h->msg_type) {
- case L1CTL_DATA_TBF_REQ:
- ul = NULL;
- rsl_chantype = RSL_CHAN_OSMO_PDCH;
+ case L1CTL_GPRS_UL_BLOCK_REQ:
+ gsmtap_chan = GSMTAP_CHANNEL_PDCH;
timeslot = tn;
subslot = 0;
- gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, 0, false);
break;
case L1CTL_TRAFFIC_REQ:
ul = (struct l1ctl_info_ul *)l1h->data;
@@ -163,85 +158,13 @@ extern void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg);
*/
extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev);
-/* determine if a received Downlink RLC/MAC block matches the current MS configuration */
-static bool gprs_dl_block_matches_ms(struct l1_model_ms *ms, struct msgb *msg, uint8_t timeslot)
-{
- uint8_t payload_type;
- uint8_t tfi;
-
- if (msgb_length(msg) < 1)
- return false;
-
- /* FIXME: Ensure this will also work for EGPRS! */
- payload_type = msg->data[0] >> 6;
- switch (payload_type) {
- case 0: /* RLC Data Block */
- /* forward all RLD Data Blocks destined for TFI of MS */
- tfi = (msg->data[1] >> 1) & 0x1f;
- if (ms->state.state == MS_STATE_TBF && ms->state.tbf.dl.tfi[timeslot] == tfi)
- return true;
- break;
- case 1: /* RLC/MAC Control without optional octets */
- /* forward all RLC/MAC control blocks without optional octets, i.e. not addressed
- * to a specific TFI */
- return true;
- case 2: /* RLC/MAC with optional control octets */
- /* forward all RLD Control Blocks destined for TFI of MS */
- tfi = (msg->data[2] >> 1) & 0x1f;
- if (ms->state.state == MS_STATE_TBF && ms->state.tbf.dl.tfi[timeslot] == tfi)
- return true;
- break;
- default:
- break;
- }
- return false;
-}
-
-/* determine if given USF at given timeslot is relevant to given MS or not */
-static bool usf_matches_ms(struct l1_model_ms *ms, uint8_t usf, uint8_t timeslot)
-{
- if (ms->state.state == MS_STATE_TBF && ms->state.tbf.ul.usf[timeslot] == usf)
- return true;
-
- return false;
-}
-
-/* extract USF from (E)GPRS RLC/MAC block */
-static uint8_t get_usf_from_block(struct msgb *msg)
-{
- /* FIXME: Ensure this will also work for EGPRS! */
- return msg->data[0] & 0x7;
-}
-
-/* MS is authorized to transmit a block in uplink for given USF on timeslot+arfcn at FN */
-static void ms_ul_tbf_may_transmit(struct l1_model_ms *ms, uint16_t arfcn, uint8_t timeslot,
- uint32_t fn, uint8_t usf)
-{
- struct msgb *msg;
-
- /* If USF is not for us, bail out */
- if (!usf_matches_ms(ms, usf, timeslot))
- return;
-
- /* attempt to de-queue pending msgb for this UL TBF and transmit it */
- msg = msgb_dequeue(&ms->state.tbf.ul.tx_queue);
- if (!msg) {
- printf("FN=%u, TN=%u, USF=%u: empty tx_queue, not transmitting\n", fn, timeslot, usf);
- /* FIXME: send some dummy control frame? */
- } else {
- printf("FN=%u, TN=%u, USF=%u: transmitting queued msg\n", fn, timeslot, usf);
- gsmtapl1_tx_to_virt_um_inst(ms, fn, timeslot, msg);
- }
-}
-
static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, uint32_t fn,
uint16_t arfcn, uint8_t timeslot, uint8_t subslot,
uint8_t gsmtap_chantype, uint8_t chan_nr, uint8_t link_id,
uint8_t snr_db)
{
struct l1_model_ms *ms = lsc->priv;
- uint8_t signal_dbm = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM)); /* Power measurement with each received massage */
- uint8_t usf;
+ uint8_t rxlev = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));
gsm_fn2gsmtime(&ms->state.downlink_time, fn);
@@ -279,7 +202,7 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
* the timeslot and subslot is fitting */
if (ms->state.dedicated.tn == timeslot
&& ms->state.dedicated.subslot == subslot) {
- l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
+ l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, rxlev, 0, 0);
}
break;
case GSMTAP_CHANNEL_VOICE_F:
@@ -289,7 +212,7 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
if (ms->state.dedicated.tn == timeslot
&& ms->state.dedicated.subslot == subslot) {
l1ctl_tx_traffic_ind(ms, msg, arfcn, link_id, chan_nr, fn,
- snr_db, signal_dbm, 0, 0);
+ snr_db, rxlev, 0, 0);
}
break;
case GSMTAP_CHANNEL_CBCH51:
@@ -303,21 +226,18 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
case GSMTAP_CHANNEL_CBCH52:
/* save to just forward here, as upper layer ignores messages that
* do not fit the current state (e.g. gsm48_rr.c:2159) */
- l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
+ l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, rxlev, 0, 0);
break;
case GSMTAP_CHANNEL_RACH:
LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in downlink ?!?\n");
break;
+ case GSMTAP_CHANNEL_PTCCH:
case GSMTAP_CHANNEL_PACCH:
case GSMTAP_CHANNEL_PDCH:
- if (gprs_dl_block_matches_ms(ms, msg, timeslot))
- l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
- usf = get_usf_from_block(msg);
- ms_ul_tbf_may_transmit(ms, arfcn, timeslot, fn, usf);
+ l1ctl_tx_gprs_dl_block_ind(ms, msg, fn, timeslot, rxlev);
break;
case GSMTAP_CHANNEL_SDCCH:
case GSMTAP_CHANNEL_CCCH:
- case GSMTAP_CHANNEL_PTCCH:
LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unsupported channel type %s\n",
get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype));
break;
diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c
index bab62590..7e4540df 100644
--- a/src/host/virt_phy/src/l1ctl_sap.c
+++ b/src/host/virt_phy/src/l1ctl_sap.c
@@ -19,6 +19,9 @@
*
*/
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
@@ -27,21 +30,16 @@
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/rsl.h>
-#include <osmocom/gprs/gprs_rlc.h>
-#include <stdio.h>
-#include <l1ctl_proto.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <virtphy/virtual_um.h>
-#include <virtphy/l1ctl_sock.h>
-#include <virtphy/virt_l1_model.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/gsmtapl1_if.h>
-#include <virtphy/logging.h>
-#include <virtphy/virt_l1_sched.h>
-static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg);
-static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg);
+#include <osmocom/bb/virtphy/virtual_um.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/l1ctl_proto.h>
+#include <osmocom/bb/l1gprs.h>
static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
{
@@ -59,7 +57,6 @@ static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
void l1ctl_sap_init(struct l1_model_ms *model)
{
INIT_LLIST_HEAD(&model->state.sched.mframe_items);
- INIT_LLIST_HEAD(&model->state.tbf.ul.tx_queue);
prim_pm_init(model);
}
@@ -162,8 +159,8 @@ static bool is_l1ctl_control(uint8_t msg_type)
switch (msg_type) {
case L1CTL_DATA_REQ:
case L1CTL_DATA_CONF:
- case L1CTL_DATA_TBF_REQ:
- case L1CTL_DATA_TBF_CONF:
+ case L1CTL_GPRS_UL_BLOCK_REQ:
+ case L1CTL_GPRS_DL_BLOCK_IND:
case L1CTL_TRAFFIC_REQ:
case L1CTL_TRAFFIC_CONF:
case L1CTL_TRAFFIC_IND:
@@ -246,11 +243,12 @@ void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg)
case L1CTL_SIM_REQ:
l1ctl_rx_sim_req(ms, msg);
break;
- case L1CTL_TBF_CFG_REQ:
- l1ctl_rx_tbf_cfg_req(ms, msg);
+ case L1CTL_GPRS_UL_TBF_CFG_REQ:
+ case L1CTL_GPRS_DL_TBF_CFG_REQ:
+ l1ctl_rx_gprs_uldl_tbf_cfg_req(ms, msg);
break;
- case L1CTL_DATA_TBF_REQ:
- l1ctl_rx_data_tbf_req(ms, msg);
+ case L1CTL_GPRS_UL_BLOCK_REQ:
+ l1ctl_rx_gprs_ul_block_req(ms, msg);
goto exit_nofree;
}
@@ -296,6 +294,12 @@ void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg)
ms->state.dedicated.subslot = subslot;
ms->state.state = MS_STATE_DEDICATED;
+ if (rsl_chantype == RSL_CHAN_OSMO_PDCH) {
+ OSMO_ASSERT(ms->gprs == NULL);
+ ms->gprs = l1gprs_state_alloc(ms, NULL, ms);
+ OSMO_ASSERT(ms->gprs != NULL);
+ }
+
/* TCH config */
if (rsl_chantype == RSL_CHAN_Bm_ACCHs || rsl_chantype == RSL_CHAN_Lm_ACCHs) {
ms->state.tch_mode = est_req->tch_mode;
@@ -380,6 +384,9 @@ void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg)
ms->state.tch_mode = GSM48_CMODE_SIGN;
ms->state.state = MS_STATE_IDLE_CAMPING;
+ l1gprs_state_free(ms->gprs);
+ ms->gprs = NULL;
+
/* TODO: disable ciphering */
/* TODO: disable audio recording / playing */
}
@@ -428,6 +435,8 @@ void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg)
DEBUGPMS(DL1C, ms, "Rx L1CTL_RESET_REQ (type=FULL)\n");
ms->state.state = MS_STATE_IDLE_SEARCHING;
virt_l1_sched_stop(ms);
+ l1gprs_state_free(ms->gprs);
+ ms->gprs = NULL;
l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type);
break;
case L1CTL_RES_T_SCHED:
@@ -486,6 +495,7 @@ void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg)
l1_model_tch_mode_set(ms, tch_mode_req->tch_mode);
ms->state.audio_mode = tch_mode_req->audio_mode;
+ /* TODO: Handle AMR codecs from tch_mode_req if tch_mode_req->tch_mode==GSM48_CMODE_SPEECH_AMR */
LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TCH_MODE_REQ (tch_mode=0x%02x audio_mode=0x%02x)\n",
tch_mode_req->tch_mode, tch_mode_req->audio_mode);
@@ -545,143 +555,12 @@ void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg)
}
-static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct l1ctl_tbf_cfg_req *in);
-
-static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg)
-{
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
- struct l1ctl_tbf_cfg_req *cfg_req = (struct l1ctl_tbf_cfg_req *) l1h->data;
- unsigned int i;
-
- LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TBF_CFG_REQ (tbf_id=%u, dir=%s, "
- "usf=[%d,%d,%d,%d,%d,%d,%d,%d]\n", cfg_req->tbf_nr,
- cfg_req->is_uplink ? "UL" : "DL", cfg_req->usf[0], cfg_req->usf[1],
- cfg_req->usf[2], cfg_req->usf[3], cfg_req->usf[4], cfg_req->usf[5],
- cfg_req->usf[6], cfg_req->usf[7]);
-
- if (cfg_req->tbf_nr != 0) {
- LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported yet!\n");
- return;
- }
-
- if (ms->state.state == MS_STATE_DEDICATED)
- LOGPMS(DL1C, LOGL_NOTICE, ms, "Hard termination of DEDICATED mode, fix L23!\n");
-
- if (cfg_req->is_uplink) {
- for (i = 0; i < 8; i++)
- ms->state.tbf.ul.usf[i] = cfg_req->usf[i];
- } else {
- for (i = 0; i < 8; i++)
- ms->state.tbf.dl.tfi[i] = cfg_req->usf[i];
- }
- ms->state.state = MS_STATE_TBF;
-
- l1ctl_tx_tbf_cfg_conf(ms, cfg_req);
-}
-
-static const enum osmo_gprs_cs osmo_cs_by_l1ctl[] = {
- [L1CTL_CS_NONE] = OSMO_GPRS_CS_NONE,
- [L1CTL_CS1] = OSMO_GPRS_CS1,
- [L1CTL_CS2] = OSMO_GPRS_CS2,
- [L1CTL_CS3] = OSMO_GPRS_CS3,
- [L1CTL_CS4] = OSMO_GPRS_CS4,
- [L1CTL_MCS1] = OSMO_GPRS_MCS1,
- [L1CTL_MCS2] = OSMO_GPRS_MCS2,
- [L1CTL_MCS3] = OSMO_GPRS_MCS3,
- [L1CTL_MCS4] = OSMO_GPRS_MCS4,
- [L1CTL_MCS5] = OSMO_GPRS_MCS5,
- [L1CTL_MCS6] = OSMO_GPRS_MCS6,
- [L1CTL_MCS7] = OSMO_GPRS_MCS7,
- [L1CTL_MCS8] = OSMO_GPRS_MCS8,
- [L1CTL_MCS9] = OSMO_GPRS_MCS9,
-};
-
-static int get_osmo_cs_by_l1ctl(enum l1ctl_coding_scheme l1)
-{
- if (l1 >= ARRAY_SIZE(osmo_cs_by_l1ctl))
- return -1;
- return osmo_cs_by_l1ctl[l1];
-}
-
-static const enum l1ctl_coding_scheme l1ctl_cs_by_osmo[] = {
- [OSMO_GPRS_CS_NONE] = L1CTL_CS_NONE,
- [OSMO_GPRS_CS1] = L1CTL_CS1,
- [OSMO_GPRS_CS2] = L1CTL_CS2,
- [OSMO_GPRS_CS3] = L1CTL_CS3,
- [OSMO_GPRS_CS4] = L1CTL_CS4,
- [OSMO_GPRS_MCS1] = L1CTL_MCS1,
- [OSMO_GPRS_MCS2] = L1CTL_MCS2,
- [OSMO_GPRS_MCS3] = L1CTL_MCS3,
- [OSMO_GPRS_MCS4] = L1CTL_MCS4,
- [OSMO_GPRS_MCS5] = L1CTL_MCS5,
- [OSMO_GPRS_MCS6] = L1CTL_MCS6,
- [OSMO_GPRS_MCS7] = L1CTL_MCS7,
- [OSMO_GPRS_MCS8] = L1CTL_MCS8,
- [OSMO_GPRS_MCS9] = L1CTL_MCS9,
-};
-
-static int get_l1ctl_cs_by_osmo(enum osmo_gprs_cs in)
-{
- if (in >= ARRAY_SIZE(l1ctl_cs_by_osmo))
- return -1;
- return l1ctl_cs_by_osmo[in];
-}
-
-static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg)
-{
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
- struct l1ctl_info_ul_tbf *udt = (struct l1ctl_info_ul_tbf *) l1h->data;
- enum osmo_gprs_cs osmo_cs;
- int block_size;
-
- msg->l2h = udt->payload;
-
- LOGPMS(DL1P, LOGL_ERROR, ms, "Rx L1CTL_DATA_TBF_REQ (tbf_id=%d, data=%s)\n",
- udt->tbf_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg)));
- if (udt->tbf_nr != 0) {
- LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported yet!\n");
- return;
- }
-
- if (ms->state.state != MS_STATE_TBF) {
- LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_TBF_REQ in state != TBF\n");
- return;
- }
-
- osmo_cs = get_l1ctl_cs_by_osmo(udt->coding_scheme);
- if (osmo_cs < 0) {
- LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_RBF_REQ with invalid CS\n");
- return;
- }
- block_size = osmo_gprs_ul_block_size_bytes(osmo_cs);
-
- if (msgb_l2len(msg) < block_size) {
- int pad_len = block_size - msgb_l2len(msg);
- uint8_t *pad = msgb_put(msg, pad_len);
- memset(pad, GSM_MACBLOCK_PADDING, pad_len);
- }
-
- msgb_enqueue(&ms->state.tbf.ul.tx_queue, msg);
-}
-
/***************************************************************
* L1CTL TX ROUTINES *******************************************
* For more routines check the respective handler classes ******
* like virt_prim_rach.c ***************************************
***************************************************************/
-static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct l1ctl_tbf_cfg_req *in)
-{
- struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TBF_CFG_CONF);
- struct l1ctl_tbf_cfg_req *out;
-
- /* copy over the data from the request */
- out = (struct l1ctl_tbf_cfg_req *) msgb_put(msg, sizeof(*out));
- *out = *in;
-
- l1ctl_sap_tx_to_l23_inst(ms, msg);
-}
-
/**
* @brief Transmit L1CTL_RESET_IND or L1CTL_RESET_CONF to layer 23.
*
diff --git a/src/host/virt_phy/src/l1ctl_sock.c b/src/host/virt_phy/src/l1ctl_sock.c
index 7951f46e..089d9caf 100644
--- a/src/host/virt_phy/src/l1ctl_sock.c
+++ b/src/host/virt_phy/src/l1ctl_sock.c
@@ -40,8 +40,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/socket.h>
-#include <virtphy/l1ctl_sock.h>
-#include <virtphy/logging.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+#include <osmocom/bb/virtphy/logging.h>
#define L1CTL_SOCK_MSGB_SIZE 256
diff --git a/src/host/virt_phy/src/l1gprs.c b/src/host/virt_phy/src/l1gprs.c
new file mode 120000
index 00000000..0185f68b
--- /dev/null
+++ b/src/host/virt_phy/src/l1gprs.c
@@ -0,0 +1 @@
+../../../shared/l1gprs.c \ No newline at end of file
diff --git a/src/host/virt_phy/src/logging.c b/src/host/virt_phy/src/logging.c
index 11a21a37..fc37205f 100644
--- a/src/host/virt_phy/src/logging.c
+++ b/src/host/virt_phy/src/logging.c
@@ -15,53 +15,49 @@
* 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 <osmocom/core/utils.h>
#include <osmocom/core/application.h>
-#include <virtphy/logging.h>
+#include <osmocom/bb/virtphy/logging.h>
static const char* l1ctlPrimNames[] = {
- "_L1CTL_NONE",
- "L1CTL_FBSB_REQ",
- "L1CTL_FBSB_CONF",
- "L1CTL_DATA_IND",
- "L1CTL_RACH_REQ",
- "L1CTL_DM_EST_REQ",
- "L1CTL_DATA_REQ",
- "L1CTL_RESET_IND",
- "L1CTL_PM_REQ",
- "L1CTL_PM_CONF",
- "L1CTL_ECHO_REQ",
- "L1CTL_ECHO_CONF",
- "L1CTL_RACH_CONF",
- "L1CTL_RESET_REQ",
- "L1CTL_RESET_CONF",
- "L1CTL_DATA_CONF",
- "L1CTL_CCCH_MODE_REQ",
- "L1CTL_CCCH_MODE_CONF",
- "L1CTL_DM_REL_REQ",
- "L1CTL_PARAM_REQ",
- "L1CTL_DM_FREQ_REQ",
- "L1CTL_CRYPTO_REQ",
- "L1CTL_SIM_REQ",
- "L1CTL_SIM_CONF",
- "L1CTL_TCH_MODE_REQ",
- "L1CTL_TCH_MODE_CONF",
- "L1CTL_NEIGH_PM_REQ",
- "L1CTL_NEIGH_PM_IND",
- "L1CTL_TRAFFIC_REQ",
- "L1CTL_TRAFFIC_CONF",
- "L1CTL_TRAFFIC_IND",
- "L1CTL_BURST_IND",
- "L1CTL_TBF_CFG_REQ",
- "L1CTL_TBF_CFG_CONF",
- "L1CTL_DATA_TBF_REQ",
- "L1CTL_DATA_TBF_CONF"
+ "_L1CTL_NONE",
+ "L1CTL_FBSB_REQ",
+ "L1CTL_FBSB_CONF",
+ "L1CTL_DATA_IND",
+ "L1CTL_RACH_REQ",
+ "L1CTL_DM_EST_REQ",
+ "L1CTL_DATA_REQ",
+ "L1CTL_RESET_IND",
+ "L1CTL_PM_REQ",
+ "L1CTL_PM_CONF",
+ "L1CTL_ECHO_REQ",
+ "L1CTL_ECHO_CONF",
+ "L1CTL_RACH_CONF",
+ "L1CTL_RESET_REQ",
+ "L1CTL_RESET_CONF",
+ "L1CTL_DATA_CONF",
+ "L1CTL_CCCH_MODE_REQ",
+ "L1CTL_CCCH_MODE_CONF",
+ "L1CTL_DM_REL_REQ",
+ "L1CTL_PARAM_REQ",
+ "L1CTL_DM_FREQ_REQ",
+ "L1CTL_CRYPTO_REQ",
+ "L1CTL_SIM_REQ",
+ "L1CTL_SIM_CONF",
+ "L1CTL_TCH_MODE_REQ",
+ "L1CTL_TCH_MODE_CONF",
+ "L1CTL_NEIGH_PM_REQ",
+ "L1CTL_NEIGH_PM_IND",
+ "L1CTL_TRAFFIC_REQ",
+ "L1CTL_TRAFFIC_CONF",
+ "L1CTL_TRAFFIC_IND",
+ "L1CTL_BURST_IND",
+ "L1CTL_GPRS_UL_TBF_CFG_REQ",
+ "L1CTL_GPRS_DL_TBF_CFG_REQ",
+ "L1CTL_GPRS_UL_BLOCK_REQ",
+ "L1CTL_GPRS_DL_BLOCK_IND",
};
static const struct log_info_cat default_categories[] = {
@@ -70,28 +66,35 @@ static const struct log_info_cat default_categories[] = {
.description = "Layer 1 Control",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_INFO,
+ .loglevel = LOGL_NOTICE,
},
[DL1P] = {
.name = "DL1P",
.description = "Layer 1 Data",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_INFO,
+ .loglevel = LOGL_NOTICE,
},
[DVIRPHY] = {
.name = "DVIRPHY",
.description = "Virtual Layer 1 Interface",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_INFO,
+ .loglevel = LOGL_NOTICE,
+ },
+ [DGPRS] = {
+ .name = "DGPRS",
+ .description = "L1 GPRS (MAC leyer)",
+ .color = "\033[1;31m",
+ .enabled = 1,
+ .loglevel = LOGL_NOTICE,
},
[DMAIN] = {
.name = "DMAIN",
.description = "Main Program / Data Structures",
.color = "\033[1;32m",
.enabled = 1,
- .loglevel = LOGL_INFO,
+ .loglevel = LOGL_NOTICE,
},
};
@@ -104,25 +107,21 @@ const struct log_info ms_log_info = {
/**
* Initialize the logging system for the virtual physical layer.
*/
-int ms_log_init(char *cat_mask)
+int ms_log_init(void *ctx, const char *cat_mask)
{
- struct log_target *stderr_target;
+ int rc;
- log_init(&ms_log_info, NULL);
- stderr_target = log_target_create_stderr();
- if (!stderr)
- return -1;
+ rc = osmo_init_logging2(ctx, &ms_log_info);
+ OSMO_ASSERT(rc == 0);
- log_add_target(stderr_target);
- log_set_all_filter(stderr_target, 1);
- //log_set_log_level(stderr_target, 1);
- log_set_print_filename2(stderr_target, LOG_FILENAME_PATH);
- log_set_use_color(stderr_target, 0);
- log_set_print_timestamp(stderr_target, 1);
- log_set_print_category_hex(stderr_target, 0);
- log_set_print_category(stderr_target, 1);
+ //log_set_log_level(osmo_stderr_target, 1);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_PATH);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_timestamp(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 1);
if (cat_mask)
- log_parse_category_mask(stderr_target, cat_mask);
+ log_parse_category_mask(osmo_stderr_target, cat_mask);
return 0;
}
diff --git a/src/host/virt_phy/src/shared/osmo_mcast_sock.c b/src/host/virt_phy/src/shared/osmo_mcast_sock.c
index d0c5b6df..d3ae4fe9 100644
--- a/src/host/virt_phy/src/shared/osmo_mcast_sock.c
+++ b/src/host/virt_phy/src/shared/osmo_mcast_sock.c
@@ -1,14 +1,16 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <talloc.h>
#include <unistd.h>
-#include <virtphy/osmo_mcast_sock.h>
+
+#include <osmocom/core/socket.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/bb/virtphy/osmo_mcast_sock.h>
/* server socket is what we use for transmission. It is not subscribed
* to a multicast group or locally bound, but it is just a normal UDP
diff --git a/src/host/virt_phy/src/shared/virtual_um.c b/src/host/virt_phy/src/shared/virtual_um.c
index ef3ad379..e55bb034 100644
--- a/src/host/virt_phy/src/shared/virtual_um.c
+++ b/src/host/virt_phy/src/shared/virtual_um.c
@@ -19,17 +19,18 @@
*
*/
+#include <unistd.h>
+#include <errno.h>
+
#include <osmocom/core/select.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
-#include <virtphy/osmo_mcast_sock.h>
-#include <virtphy/virtual_um.h>
-#include <unistd.h>
-#include <errno.h>
+#include <osmocom/bb/virtphy/osmo_mcast_sock.h>
+#include <osmocom/bb/virtphy/virtual_um.h>
/**
* Virtual UM interface file descriptor callback.
diff --git a/src/host/virt_phy/src/virt_l1_model.c b/src/host/virt_phy/src/virt_l1_model.c
index 5738bed6..704a54cd 100644
--- a/src/host/virt_phy/src/virt_l1_model.c
+++ b/src/host/virt_phy/src/virt_l1_model.c
@@ -19,11 +19,12 @@
*
*/
-#include <virtphy/virt_l1_model.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/logging.h>
#include <talloc.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/logging.h>
+
static uint32_t next_ms_nr;
struct l1_model_ms *l1_model_ms_init(void *ctx, struct l1ctl_sock_client *lsc, struct virt_um_inst *vui)
diff --git a/src/host/virt_phy/src/virt_l1_sched_simple.c b/src/host/virt_phy/src/virt_l1_sched_simple.c
index 486d319a..3cad2ce0 100644
--- a/src/host/virt_phy/src/virt_l1_sched_simple.c
+++ b/src/host/virt_phy/src/virt_l1_sched_simple.c
@@ -18,13 +18,15 @@
*
*/
-#include <virtphy/virt_l1_sched.h>
-#include <osmocom/core/linuxlist.h>
-#include <virtphy/virt_l1_model.h>
-#include <virtphy/logging.h>
#include <time.h>
#include <talloc.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/virtphy/logging.h>
+
/**
* @brief Start scheduler thread based on current gsm time from model
*/
diff --git a/src/host/virt_phy/src/virt_prim_data.c b/src/host/virt_phy/src/virt_prim_data.c
index 656ff800..d8897915 100644
--- a/src/host/virt_phy/src/virt_prim_data.c
+++ b/src/host/virt_phy/src/virt_prim_data.c
@@ -16,10 +16,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 <stdint.h>
@@ -31,12 +27,12 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/core/msgb.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/virt_l1_sched.h>
-#include <virtphy/logging.h>
-#include <virtphy/gsmtapl1_if.h>
-#include <l1ctl_proto.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/l1ctl_proto.h>
/**
* @brief Handler callback function for DATA request.
@@ -81,8 +77,8 @@ void l1ctl_rx_data_req(struct l1_model_ms *ms, struct msgb *msg)
}
void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
- uint8_t chan_nr, uint32_t fn, uint8_t snr,
- uint8_t signal_dbm, uint8_t num_biterr, uint8_t fire_crc)
+ uint8_t chan_nr, uint32_t fn, uint8_t snr,
+ uint8_t rxlev, uint8_t num_biterr, uint8_t fire_crc)
{
struct msgb *l1ctl_msg = NULL;
struct l1ctl_data_ind * l1di;
@@ -98,7 +94,7 @@ void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn,
l1dl->chan_nr = chan_nr;
l1dl->frame_nr = htonl(fn);
l1dl->snr = snr;
- l1dl->rx_level = signal_dbm;
+ l1dl->rx_level = rxlev;
l1dl->num_biterr = 0; /* no biterrors */
l1dl->fire_crc = 0;
diff --git a/src/host/virt_phy/src/virt_prim_fbsb.c b/src/host/virt_phy/src/virt_prim_fbsb.c
index c14a4485..d012036f 100644
--- a/src/host/virt_phy/src/virt_prim_fbsb.c
+++ b/src/host/virt_phy/src/virt_prim_fbsb.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 <stdint.h>
@@ -29,11 +25,12 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/core/msgb.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/virt_l1_sched.h>
#include <osmocom/core/gsmtap.h>
-#include <virtphy/logging.h>
-#include <l1ctl_proto.h>
+
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/l1ctl_proto.h>
static uint16_t sync_count = 0;
diff --git a/src/host/virt_phy/src/virt_prim_pdch.c b/src/host/virt_phy/src/virt_prim_pdch.c
new file mode 100644
index 00000000..3fa7977b
--- /dev/null
+++ b/src/host/virt_phy/src/virt_prim_pdch.c
@@ -0,0 +1,108 @@
+/*
+ * (C) 2023 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 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.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/virtphy/logging.h>
+
+#include <osmocom/bb/l1ctl_proto.h>
+#include <osmocom/bb/l1gprs.h>
+
+void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
+
+ if (OSMO_UNLIKELY(ms->gprs == NULL)) {
+ LOGPMS(DL1C, LOGL_ERROR, ms, "l1gprs is not initialized\n");
+ return;
+ }
+
+ msg->l1h = msgb_pull(msg, sizeof(*l1h));
+
+ if (l1h->msg_type == L1CTL_GPRS_UL_TBF_CFG_REQ)
+ l1gprs_handle_ul_tbf_cfg_req(ms->gprs, msg);
+ else
+ l1gprs_handle_dl_tbf_cfg_req(ms->gprs, msg);
+}
+
+void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg)
+{
+ const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
+ struct l1gprs_prim_ul_block_req req;
+
+ if (OSMO_UNLIKELY(ms->gprs == NULL)) {
+ LOGPMS(DL1P, LOGL_ERROR, ms, "l1gprs is not initialized\n");
+ msgb_free(msg);
+ return;
+ }
+
+ msg->l1h = (void *)l1h->data;
+ if (l1gprs_handle_ul_block_req(ms->gprs, &req, msg) != 0) {
+ msgb_free(msg);
+ return;
+ }
+ msg->l2h = (void *)&req.data[0];
+
+ virt_l1_sched_schedule(ms, msg, req.hdr.fn, req.hdr.tn,
+ &gsmtapl1_tx_to_virt_um_inst);
+}
+
+void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg,
+ uint32_t fn, uint8_t tn, uint8_t rxlev)
+{
+ struct l1gprs_prim_dl_block_ind ind;
+ struct msgb *nmsg;
+ uint8_t usf = 0xff;
+
+ if (ms->gprs == NULL)
+ return;
+
+ ind = (struct l1gprs_prim_dl_block_ind) {
+ .hdr = {
+ .fn = fn,
+ .tn = tn,
+ },
+ .meas = {
+ .ber10k = 0, /* perfect Um, no errors */
+ .ci_cb = 180, /* 18 dB */
+ .rx_lev = rxlev,
+ },
+ .data = msgb_data(msg),
+ .data_len = msgb_length(msg),
+ };
+
+ nmsg = l1gprs_handle_dl_block_ind(ms->gprs, &ind, &usf);
+ if (nmsg != NULL)
+ l1ctl_sap_tx_to_l23_inst(ms, nmsg);
+ /* Every fn % 13 == 12 we have either a PTCCH or an IDLE slot, thus
+ * every fn % 13 == 8 we add 5 frames, or 4 frames othrwise. The
+ * resulting value is first fn of the next block. */
+ const uint32_t rts_fn = GSM_TDMA_FN_SUM(fn, (fn % 13 == 8) ? 5 : 4);
+ nmsg = l1gprs_handle_rts_ind(ms->gprs, rts_fn, tn, usf);
+ if (nmsg != NULL)
+ l1ctl_sap_tx_to_l23_inst(ms, nmsg);
+}
diff --git a/src/host/virt_phy/src/virt_prim_pm.c b/src/host/virt_phy/src/virt_prim_pm.c
index 08d9b054..5883bbbe 100644
--- a/src/host/virt_phy/src/virt_prim_pm.c
+++ b/src/host/virt_phy/src/virt_prim_pm.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 <stdint.h>
@@ -26,14 +22,15 @@
#include <string.h>
#include <stdlib.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/core/msgb.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/virt_l1_sched.h>
#include <osmocom/core/gsmtap.h>
-#include <virtphy/logging.h>
-#include <l1ctl_proto.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/l1ctl_proto.h>
/**
* @brief Change the signal strength for a given arfcn.
diff --git a/src/host/virt_phy/src/virt_prim_rach.c b/src/host/virt_phy/src/virt_prim_rach.c
index 94076cf9..d12e63c6 100644
--- a/src/host/virt_phy/src/virt_prim_rach.c
+++ b/src/host/virt_phy/src/virt_prim_rach.c
@@ -16,10 +16,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 <stdint.h>
@@ -27,16 +23,17 @@
#include <string.h>
#include <stdlib.h>
+#include <osmocom/core/msgb.h>
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include <osmocom/core/msgb.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/virt_l1_sched.h>
-#include <virtphy/logging.h>
-#include <virtphy/gsmtapl1_if.h>
-#include <l1ctl_proto.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/l1ctl_proto.h>
/* use if we have a combined uplink (RACH, SDCCH, ...) (see
* http://www.rfwireless-world.com/Terminology/GSM-combined-channel-configuration.html)
@@ -79,36 +76,37 @@ void l1ctl_rx_rach_req(struct l1_model_ms *ms, struct msgb *msg)
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload;
uint32_t fn_sched;
- uint8_t ts = 1; /* FIXME mostly, ts 1 is used for rach, where can i get that info? System info? */
uint16_t offset = ntohs(rach_req->offset);
LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n",
rach_req->ra, offset, rach_req->combined);
- if (rach_req->ra == 0x03)
- fn_sched = 42;
-
/* set ra data to msg (8bits, the 11bit option is not used for GSM) */
msg->l2h = msgb_put(msg, sizeof(uint8_t));
*msg->l2h = rach_req->ra;
- /* chan_nr need to be encoded here, as it is not set by l23 for
- * the rach request, but needed by virt um */
- ul->chan_nr = rsl_enc_chan_nr(RSL_CHAN_RACH, 0, ts);
- ul->link_id = LID_DEDIC;
+ /* use the indicated RSL chan_nr/link_id, if provided */
+ if (ul->chan_nr == 0x00) {
+ LOGPMS(DL1C, LOGL_NOTICE, ms,
+ "The UL info header is empty, assuming RACH is on TS0\n");
+ ul->chan_nr = RSL_CHAN_RACH;
+ ul->link_id = LID_DEDIC;
+ }
/* sched fn calculation if we have a combined ccch channel configuration */
if (rach_req->combined) {
/* add elapsed RACH slots to offset */
offset += t3_to_rach_comb[l1s->current_time.t3];
/* offset is the number of RACH slots in the future */
- fn_sched = l1s->current_time.fn - l1s->current_time.t3;
+ fn_sched = GSM_TDMA_FN_SUB(l1s->current_time.fn, l1s->current_time.t3);
fn_sched += offset / 27 * 51;
fn_sched += rach_to_t3_comb[offset % 27];
+ fn_sched %= GSM_TDMA_HYPERFRAME;
} else
- fn_sched = l1s->current_time.fn + offset;
+ fn_sched = GSM_TDMA_FN_SUM(l1s->current_time.fn, offset);
- virt_l1_sched_schedule(ms, msg, fn_sched, ts, &virt_l1_sched_handler_cb);
+ virt_l1_sched_schedule(ms, msg, fn_sched, ul->chan_nr & 0x07,
+ &virt_l1_sched_handler_cb);
}
/**
diff --git a/src/host/virt_phy/src/virt_prim_traffic.c b/src/host/virt_phy/src/virt_prim_traffic.c
index 0e08a126..778d67a3 100644
--- a/src/host/virt_phy/src/virt_prim_traffic.c
+++ b/src/host/virt_phy/src/virt_prim_traffic.c
@@ -16,10 +16,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 <stdint.h>
@@ -27,16 +23,16 @@
#include <string.h>
#include <stdlib.h>
+#include <osmocom/core/msgb.h>
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include <osmocom/core/msgb.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/virt_l1_sched.h>
-#include <virtphy/logging.h>
-#include <virtphy/gsmtapl1_if.h>
-#include <l1ctl_proto.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/l1ctl_proto.h>
/**
* @brief Handler callback function for TRAFFIC request.
@@ -79,7 +75,7 @@ void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg)
}
void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
- uint8_t chan_nr, uint32_t fn, uint8_t snr, uint8_t signal_dbm,
+ uint8_t chan_nr, uint32_t fn, uint8_t snr, uint8_t rxlev,
uint8_t num_biterr, uint8_t fire_crc)
{
struct msgb *l1ctl_msg = NULL;
@@ -99,7 +95,7 @@ void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arf
l1dl->chan_nr = chan_nr;
l1dl->frame_nr = htonl(fn);
l1dl->snr = snr;
- l1dl->rx_level = signal_dbm;
+ l1dl->rx_level = rxlev;
l1dl->num_biterr = 0; /* no biterrors */
l1dl->fire_crc = 0;
diff --git a/src/host/virt_phy/src/virtphy.c b/src/host/virt_phy/src/virtphy.c
index 412da9c8..0aa21adc 100644
--- a/src/host/virt_phy/src/virtphy.c
+++ b/src/host/virt_phy/src/virtphy.c
@@ -20,10 +20,6 @@
*
*/
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/gsmtap.h>
-#include <osmocom/core/application.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
@@ -31,15 +27,22 @@
#include <getopt.h>
#include <errno.h>
#include <signal.h>
-#include <virtphy/virtual_um.h>
-#include <virtphy/l1ctl_sock.h>
-#include <virtphy/virt_l1_model.h>
-#include <virtphy/l1ctl_sap.h>
-#include <virtphy/gsmtapl1_if.h>
-#include <virtphy/logging.h>
-#include <virtphy/virt_l1_sched.h>
-#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DMAIN,1"
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/application.h>
+
+#include <osmocom/bb/virtphy/virtual_um.h>
+#include <osmocom/bb/virtphy/l1ctl_sock.h>
+#include <osmocom/bb/virtphy/virt_l1_model.h>
+#include <osmocom/bb/virtphy/l1ctl_sap.h>
+#include <osmocom/bb/virtphy/gsmtapl1_if.h>
+#include <osmocom/bb/virtphy/logging.h>
+#include <osmocom/bb/virtphy/virt_l1_sched.h>
+#include <osmocom/bb/l1gprs.h>
+
+#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DGPRS,1:DMAIN,1"
/* this exists once in the program, and contains the state that we
* only keep once: L1CTL server socket, GSMTAP/VirtUM socket */
@@ -62,12 +65,12 @@ static char *pm_timeout = NULL;
static char *mcast_netdev = NULL;
static int mcast_ttl = -1;
-static void print_usage()
+static void print_usage(void)
{
printf("Usage: virtphy\n");
}
-static void print_help()
+static void print_help(void)
{
printf(" Some useful help...\n");
printf(" -h --help This text.\n");
@@ -239,7 +242,8 @@ int main(int argc, char *argv[])
/* init loginfo */
handle_options(argc, argv);
- ms_log_init(log_mask);
+ ms_log_init(tall_vphy_ctx, log_mask);
+ l1gprs_logging_init(DGPRS);
LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer starting up...\n");
diff --git a/src/shared/l1gprs.c b/src/shared/l1gprs.c
new file mode 100644
index 00000000..2bf759d5
--- /dev/null
+++ b/src/shared/l1gprs.c
@@ -0,0 +1,831 @@
+/*
+ * l1gprs - GPRS layer 1 implementation
+ *
+ * (C) 2022-2024 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 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.
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/bb/l1ctl_proto.h>
+#include <osmocom/bb/l1gprs.h>
+
+#define LOGP_GPRS(gprs, level, fmt, args...) \
+ LOGP(l1gprs_log_cat, level, "%s" fmt, \
+ (gprs)->log_prefix, ## args)
+
+#define LOGP_PDCH(pdch, level, fmt, args...) \
+ LOGP_GPRS((pdch)->gprs, level, "(PDCH-%u) " fmt, \
+ (pdch)->tn, ## args)
+
+#define LOG_TBF_CFG_REQ_FMT "tbf_ref=%u, slotmask=0x%02x, start_fn=%u"
+#define LOG_TBF_CFG_REQ_ARGS(req) \
+ (req)->tbf_ref, (req)->slotmask, ntohl((req)->start_fn)
+
+#define LOG_TBF_FMT "%cL-TBF#%03d(slotmask=0x%02x)"
+#define LOG_TBF_ARGS(tbf) \
+ (tbf)->uplink ? 'U' : 'D', (tbf)->tbf_ref, (tbf)->slotmask
+
+#define TDMA_FN_INVALID 0xffffffff
+
+static int l1gprs_log_cat = DLGLOBAL;
+
+enum gprs_rlcmac_block_type {
+ GPRS_RLCMAC_DATA_BLOCK = 0x00,
+ GPRS_RLCMAC_CONTROL_BLOCK = 0x01,
+ GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x02,
+ GPRS_RLCMAC_RESERVED = 0x03,
+};
+
+static struct l1gprs_tbf_pending_req *l1gprs_tbf_pending_req_alloc(void *talloc_ctx,
+ bool uplink, uint8_t tbf_ref,
+ uint8_t slotmask, uint32_t start_fn)
+{
+ struct l1gprs_tbf_pending_req *preq;
+
+ preq = talloc(talloc_ctx, struct l1gprs_tbf_pending_req);
+ OSMO_ASSERT(preq != NULL);
+
+ preq->uplink = uplink;
+ preq->tbf_ref = tbf_ref;
+ preq->slotmask = slotmask;
+ preq->start_fn = start_fn;
+
+ return preq;
+}
+
+static void l1gprs_tbf_pending_req_free(struct l1gprs_tbf_pending_req *preq)
+{
+ if (preq == NULL)
+ return;
+ llist_del(&preq->list);
+ talloc_free(preq);
+}
+
+
+static struct l1gprs_tbf *l1gprs_tbf_alloc(void *talloc_ctx,
+ bool uplink, uint8_t tbf_ref,
+ uint8_t slotmask)
+{
+ struct l1gprs_tbf *tbf;
+
+ tbf = talloc(talloc_ctx, struct l1gprs_tbf);
+ OSMO_ASSERT(tbf != NULL);
+
+ tbf->uplink = uplink;
+ tbf->tbf_ref = tbf_ref;
+ tbf->slotmask = slotmask;
+
+ return tbf;
+}
+
+static void l1gprs_tbf_free(struct l1gprs_tbf *tbf)
+{
+ if (tbf == NULL)
+ return;
+ llist_del(&tbf->list);
+ talloc_free(tbf);
+}
+
+static struct l1gprs_tbf *_l1gprs_find_tbf(const struct llist_head *tbf_list,
+ bool uplink, uint8_t tbf_ref)
+{
+ struct l1gprs_tbf *tbf;
+
+ llist_for_each_entry(tbf, tbf_list, list) {
+ if (tbf->uplink != uplink)
+ continue;
+ if (tbf->tbf_ref != tbf_ref)
+ continue;
+ return tbf;
+ }
+
+ return NULL;
+}
+
+static struct l1gprs_tbf *l1gprs_find_tbf(struct l1gprs_state *gprs,
+ bool uplink, uint8_t tbf_ref)
+{
+ struct l1gprs_tbf *tbf;
+
+ if ((tbf = _l1gprs_find_tbf(&gprs->tbf_list, uplink, tbf_ref)) != NULL)
+ return tbf;
+ return NULL;
+}
+
+static void l1gprs_register_tbf(struct l1gprs_state *gprs,
+ struct l1gprs_tbf *tbf)
+{
+ OSMO_ASSERT(tbf->slotmask != 0x00);
+
+ /* Update the PDCH states */
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
+ struct l1gprs_pdch *pdch = &gprs->pdch[tn];
+
+ if (~tbf->slotmask & (1 << pdch->tn))
+ continue;
+
+ if (tbf->uplink) {
+ pdch->ul_tbf_count++;
+ } else {
+ pdch->dl_tbf_count++;
+ pdch->dl_tfi_mask |= (1 << tbf->dl_tfi);
+ }
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Linked " LOG_TBF_FMT "\n",
+ LOG_TBF_ARGS(tbf));
+
+ /* If just got first use: */
+ if (l1gprs_pdch_use_count(pdch) == 1) {
+ if (gprs->pdch_changed_cb)
+ gprs->pdch_changed_cb(pdch, true);
+ }
+ }
+
+ llist_add_tail(&tbf->list, &gprs->tbf_list);
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ LOG_TBF_FMT " is registered as active\n",
+ LOG_TBF_ARGS(tbf));
+}
+
+static void l1gprs_update_tbf(struct l1gprs_state *gprs, struct l1gprs_tbf *tbf, uint8_t slotmask)
+{
+ OSMO_ASSERT(tbf->slotmask != 0x00);
+ OSMO_ASSERT(slotmask != 0x00);
+
+ if (tbf->slotmask == slotmask)
+ return; /* No change at all, skip */
+
+ /* Update the PDCH states */
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
+ struct l1gprs_pdch *pdch = &gprs->pdch[tn];
+
+ if ((tbf->slotmask & (1 << tn)) == (slotmask & (1 << tn)))
+ continue; /* No change, skip */
+
+ if (tbf->slotmask & (1 << tn)) {
+ /* slot previously set, remove it */
+ if (tbf->uplink) {
+ OSMO_ASSERT(pdch->ul_tbf_count > 0);
+ pdch->ul_tbf_count--;
+ } else {
+ OSMO_ASSERT(pdch->dl_tbf_count > 0);
+ pdch->dl_tbf_count--;
+ pdch->dl_tfi_mask &= ~(1 << tbf->dl_tfi);
+ }
+ LOGP_PDCH(pdch, LOGL_DEBUG, "Unlinked " LOG_TBF_FMT "\n",
+ LOG_TBF_ARGS(tbf));
+ /* If not more in use: */
+ if (l1gprs_pdch_use_count(pdch) == 0) {
+ if (gprs->pdch_changed_cb)
+ gprs->pdch_changed_cb(pdch, false);
+ }
+ } else {
+ /* Slot was not set, add it */
+ if (tbf->uplink) {
+ pdch->ul_tbf_count++;
+ } else {
+ pdch->dl_tbf_count++;
+ pdch->dl_tfi_mask |= (1 << tbf->dl_tfi);
+ }
+ LOGP_PDCH(pdch, LOGL_DEBUG, "Linked " LOG_TBF_FMT "\n",
+ LOG_TBF_ARGS(tbf));
+ /* If just got first use: */
+ if (l1gprs_pdch_use_count(pdch) == 1) {
+ if (gprs->pdch_changed_cb)
+ gprs->pdch_changed_cb(pdch, true);
+ }
+ }
+ }
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ LOG_TBF_FMT " slotmask updated 0x%02x -> 0x%02x\n",
+ LOG_TBF_ARGS(tbf), tbf->slotmask, slotmask);
+
+ tbf->slotmask = slotmask;
+}
+
+static void l1gprs_unregister_tbf(struct l1gprs_state *gprs, struct l1gprs_tbf *tbf)
+{
+ OSMO_ASSERT(tbf->slotmask != 0x00);
+
+ /* Update the PDCH states */
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
+ struct l1gprs_pdch *pdch = &gprs->pdch[tn];
+
+ if (~tbf->slotmask & (1 << pdch->tn))
+ continue;
+
+ if (tbf->uplink) {
+ OSMO_ASSERT(pdch->ul_tbf_count > 0);
+ pdch->ul_tbf_count--;
+ } else {
+ OSMO_ASSERT(pdch->dl_tbf_count > 0);
+ pdch->dl_tbf_count--;
+ pdch->dl_tfi_mask &= ~(1 << tbf->dl_tfi);
+ }
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Unlinked " LOG_TBF_FMT "\n",
+ LOG_TBF_ARGS(tbf));
+
+ /* If not more in use: */
+ if (l1gprs_pdch_use_count(pdch) == 0) {
+ if (gprs->pdch_changed_cb)
+ gprs->pdch_changed_cb(pdch, false);
+ }
+ }
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ LOG_TBF_FMT " is unregistered and free()d\n",
+ LOG_TBF_ARGS(tbf));
+
+ l1gprs_tbf_free(tbf);
+}
+
+static void l1gprs_add_tbf_pending_req(struct l1gprs_state *gprs, struct l1gprs_tbf_pending_req *preq)
+{
+ OSMO_ASSERT(preq->slotmask != 0x00);
+
+ /* Update the PDCH states */
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
+ struct l1gprs_pdch *pdch = &gprs->pdch[tn];
+
+ if (~preq->slotmask & (1 << pdch->tn))
+ continue;
+
+ if (preq->uplink) {
+ pdch->pending_ul_tbf_count++;
+ } else {
+ pdch->pending_dl_tbf_count++;
+ /* We don't care about DL_TFI here, we don't want to activate it */
+ }
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Linked " LOG_TBF_FMT "\n",
+ LOG_TBF_ARGS(preq));
+ /* If just got first use: */
+ if (l1gprs_pdch_use_count(pdch) == 1) {
+ if (gprs->pdch_changed_cb)
+ gprs->pdch_changed_cb(pdch, true);
+ }
+ }
+
+ llist_add_tail(&preq->list, &gprs->tbf_list_pending);
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ LOG_TBF_FMT " is added as pending (fn=%u)\n",
+ LOG_TBF_ARGS(preq), preq->start_fn);
+}
+
+static void l1gprs_remove_tbf_pending_req(struct l1gprs_state *gprs, struct l1gprs_tbf_pending_req *preq)
+{
+
+ OSMO_ASSERT(preq->slotmask != 0x00);
+
+ /* Update the PDCH states */
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
+ struct l1gprs_pdch *pdch = &gprs->pdch[tn];
+
+ if (~preq->slotmask & (1 << pdch->tn))
+ continue;
+
+ if (preq->uplink) {
+ OSMO_ASSERT(pdch->pending_ul_tbf_count > 0);
+ pdch->pending_ul_tbf_count--;
+ } else {
+ OSMO_ASSERT(pdch->pending_dl_tbf_count > 0);
+ pdch->pending_dl_tbf_count--;
+ /* We don't care about DL_TFI here, we didn't activate them in first place */
+ }
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Unlinked " LOG_TBF_FMT "\n",
+ LOG_TBF_ARGS(preq));
+ /* Note: not calling gprs->pdch_changed_cb since no real
+ * activate / deactivate change can occur on lower layers as a
+ * consequence of moving a PDCH from pending to active, hence
+ * avoid triggering one active=false event here and immediately
+ * afterwards the opposite event when adding it as active: */
+ }
+
+ llist_del(&preq->list);
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ LOG_TBF_FMT " is removed as pending (fn=%u)\n",
+ LOG_TBF_ARGS(preq), preq->start_fn);
+}
+
+/* Check the list of pending TBFs and move those with expired Fn to the active list */
+static void l1gprs_check_pending_tbfs(struct l1gprs_state *gprs, uint32_t fn)
+{
+ struct l1gprs_tbf_pending_req *preq, *tmp;
+ struct l1gprs_tbf *tbf;
+
+ llist_for_each_entry_safe(preq, tmp, &gprs->tbf_list_pending, list) {
+ if (gsm0502_fncmp(fn, preq->start_fn) < 0)
+ continue;
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ LOG_TBF_FMT " becomes active (current_fn=%u, start_fn=%u)\n",
+ LOG_TBF_ARGS(preq), fn, preq->start_fn);
+
+ l1gprs_remove_tbf_pending_req(gprs, preq);
+
+ /* If this tbf already exists in the main list, simply update its timeslot: */
+ tbf = _l1gprs_find_tbf(&gprs->tbf_list, preq->uplink, preq->tbf_ref);
+ if (tbf) {
+ l1gprs_update_tbf(gprs, tbf, preq->slotmask);
+ tbf->dl_tfi = preq->dl_tfi;
+ } else {
+ tbf = l1gprs_tbf_alloc(gprs, preq->uplink, preq->tbf_ref, preq->slotmask);
+ tbf->dl_tfi = preq->dl_tfi;
+ l1gprs_register_tbf(gprs, tbf);
+ }
+ talloc_free(preq);
+ }
+}
+
+#define L1GPRS_L1CTL_MSGB_SIZE 256
+#define L1GPRS_L1CTL_MSGB_HEADROOM 32
+
+static struct msgb *l1gprs_l1ctl_msgb_alloc(uint8_t msg_type)
+{
+ struct l1ctl_hdr *l1h;
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(L1GPRS_L1CTL_MSGB_SIZE,
+ L1GPRS_L1CTL_MSGB_HEADROOM,
+ "l1gprs_l1ctl_msg");
+ if (msg == NULL)
+ return NULL;
+
+ msg->l1h = msgb_put(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *)msg->l1h;
+ l1h->msg_type = msg_type;
+
+ return msg;
+}
+
+static bool l1gprs_pdch_filter_dl_block(const struct l1gprs_pdch *pdch,
+ const uint8_t *data)
+{
+ enum gprs_rlcmac_block_type block_type = data[0] >> 6;
+ uint8_t dl_tfi;
+
+ switch (block_type) {
+ case GPRS_RLCMAC_DATA_BLOCK:
+ /* see 3GPP TS 44.060, section 10.2.1 */
+ dl_tfi = (data[1] >> 1) & 0x1f;
+ break;
+ case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
+ /* see 3GPP TS 44.060, section 10.3.1 */
+ dl_tfi = (data[2] >> 1) & 0x1f;
+ break;
+ case GPRS_RLCMAC_CONTROL_BLOCK:
+ /* no optional octets */
+ return true;
+ default:
+ LOGP_PDCH(pdch, LOGL_NOTICE,
+ "Rx Downlink block with unknown payload (0x%0x)\n",
+ block_type);
+ return false;
+ }
+
+ if (pdch->dl_tfi_mask & (1 << dl_tfi))
+ return true;
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+void l1gprs_logging_init(int logc)
+{
+ l1gprs_log_cat = logc;
+}
+
+struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv)
+{
+ struct l1gprs_state *gprs;
+
+ gprs = talloc_zero(ctx, struct l1gprs_state);
+ if (gprs == NULL)
+ return NULL;
+
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) {
+ struct l1gprs_pdch *pdch = &gprs->pdch[tn];
+
+ pdch->tn = tn;
+ pdch->gprs = gprs;
+ }
+
+ INIT_LLIST_HEAD(&gprs->tbf_list);
+ INIT_LLIST_HEAD(&gprs->tbf_list_pending);
+
+ if (log_prefix == NULL)
+ gprs->log_prefix = talloc_asprintf(gprs, "l1gprs[0x%p]: ", gprs);
+ else
+ gprs->log_prefix = talloc_strdup(gprs, log_prefix);
+ gprs->priv = priv;
+
+ return gprs;
+}
+
+void l1gprs_state_free(struct l1gprs_state *gprs)
+{
+ if (gprs == NULL)
+ return;
+
+ while (!llist_empty(&gprs->tbf_list)) {
+ struct l1gprs_tbf *tbf;
+
+ tbf = llist_first_entry(&gprs->tbf_list, struct l1gprs_tbf, list);
+ LOGP_GPRS(gprs, LOGL_DEBUG,
+ "%s(): " LOG_TBF_FMT " is free()d\n",
+ __func__, LOG_TBF_ARGS(tbf));
+ l1gprs_tbf_free(tbf);
+ }
+
+ while (!llist_empty(&gprs->tbf_list_pending)) {
+ struct l1gprs_tbf_pending_req *preq;
+
+ preq = llist_first_entry(&gprs->tbf_list_pending, struct l1gprs_tbf_pending_req, list);
+ LOGP_GPRS(gprs, LOGL_DEBUG,
+ "%s(): " LOG_TBF_FMT " is free()d\n",
+ __func__, LOG_TBF_ARGS(preq));
+ l1gprs_tbf_pending_req_free(preq);
+ }
+
+ talloc_free(gprs->log_prefix);
+ talloc_free(gprs);
+}
+
+void l1gprs_state_set_pdch_changed_cb(struct l1gprs_state *gprs, l1gprs_pdch_changed_t pdch_changed_cb)
+{
+ gprs->pdch_changed_cb = pdch_changed_cb;
+}
+
+int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg)
+{
+ const struct l1ctl_gprs_ul_tbf_cfg_req *req = (void *)msg->l1h;
+ struct l1gprs_tbf *tbf = NULL;
+
+ OSMO_ASSERT(req != NULL);
+
+ if (msgb_l1len(msg) < sizeof(*req)) {
+ LOGP_GPRS(gprs, LOGL_ERROR,
+ "Rx malformed Uplink TBF config (len=%u < %zu)\n",
+ msgb_l1len(msg), sizeof(*req));
+ return -EINVAL;
+ }
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ "Rx UL TBF config: " LOG_TBF_CFG_REQ_FMT "\n",
+ LOG_TBF_CFG_REQ_ARGS(req));
+
+ if (req->slotmask != 0x00) {
+ uint32_t start_fn = ntohl(req->start_fn);
+ if (start_fn != TDMA_FN_INVALID) {
+ /* Create a temporary tbf and keep it in a separate
+ * list. It will be moved/merged into the main list at
+ * start_fn time. */
+ struct l1gprs_tbf_pending_req *preq;
+ preq = l1gprs_tbf_pending_req_alloc(gprs, true, req->tbf_ref,
+ req->slotmask, start_fn);
+ l1gprs_add_tbf_pending_req(gprs, preq);
+ return 0;
+ }
+
+ tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref);
+ if (tbf) {
+ l1gprs_update_tbf(gprs, tbf, req->slotmask);
+ } else {
+ tbf = l1gprs_tbf_alloc(gprs, true, req->tbf_ref, req->slotmask);
+ l1gprs_register_tbf(gprs, tbf);
+ }
+ } else {
+ tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref);
+ if (tbf == NULL) {
+ LOGP_GPRS(gprs, LOGL_ERROR, "%s(): " LOG_TBF_FMT " not found\n",
+ __func__, 'U', req->tbf_ref, req->slotmask);
+ return -ENOENT;
+ }
+ l1gprs_unregister_tbf(gprs, tbf);
+ }
+
+ return 0;
+}
+
+int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg)
+{
+ const struct l1ctl_gprs_dl_tbf_cfg_req *req = (void *)msg->l1h;
+ struct l1gprs_tbf *tbf = NULL;
+
+ OSMO_ASSERT(req != NULL);
+
+ if (msgb_l1len(msg) < sizeof(*req)) {
+ LOGP_GPRS(gprs, LOGL_ERROR,
+ "Rx malformed Downlink TBF config (len=%u < %zu)\n",
+ msgb_l1len(msg), sizeof(*req));
+ return -EINVAL;
+ }
+
+ LOGP_GPRS(gprs, LOGL_INFO,
+ "Rx DL TBF config: " LOG_TBF_CFG_REQ_FMT ", dl_tfi=%u\n",
+ LOG_TBF_CFG_REQ_ARGS(req), req->dl_tfi);
+
+ if (req->dl_tfi > 31) {
+ LOGP_GPRS(gprs, LOGL_ERROR,
+ "Invalid DL TFI %u (shall be in range 0..31)\n",
+ req->dl_tfi);
+ return -EINVAL;
+ }
+
+ if (req->slotmask != 0x00) {
+ uint32_t start_fn = ntohl(req->start_fn);
+ if (start_fn != TDMA_FN_INVALID) {
+ /* Create a temporary tbf and keep it in a separate
+ * list. It will be moved/merged into the main list at
+ * start_fn time. */
+ struct l1gprs_tbf_pending_req *preq;
+ preq = l1gprs_tbf_pending_req_alloc(gprs, false, req->tbf_ref,
+ req->slotmask, start_fn);
+ preq->dl_tfi = req->dl_tfi;
+ l1gprs_add_tbf_pending_req(gprs, preq);
+ return 0;
+ }
+
+ tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref);
+ if (tbf) {
+ l1gprs_update_tbf(gprs, tbf, req->slotmask);
+ } else {
+ tbf = l1gprs_tbf_alloc(gprs, false, req->tbf_ref,
+ req->slotmask);
+ tbf->dl_tfi = req->dl_tfi;
+ l1gprs_register_tbf(gprs, tbf);
+ }
+ } else {
+ tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref);
+ if (tbf == NULL) {
+ LOGP_GPRS(gprs, LOGL_ERROR, "%s(): " LOG_TBF_FMT " not found\n",
+ __func__, 'D', req->tbf_ref, req->slotmask);
+ return -ENOENT;
+ }
+ l1gprs_unregister_tbf(gprs, tbf);
+ }
+
+ return 0;
+}
+
+int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs,
+ struct l1gprs_prim_ul_block_req *req,
+ const struct msgb *msg)
+{
+ const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h;
+ const struct l1gprs_pdch *pdch = NULL;
+ size_t data_len;
+ uint32_t fn;
+
+ OSMO_ASSERT(l1br != NULL);
+
+ if (OSMO_UNLIKELY(msgb_l1len(msg) < sizeof(*l1br))) {
+ LOGP_GPRS(gprs, LOGL_ERROR,
+ "Rx malformed UL BLOCK.req (len=%u < %zu)\n",
+ msgb_l1len(msg), sizeof(*l1br));
+ return -EINVAL;
+ }
+ fn = ntohl(l1br->hdr.fn);
+ if (OSMO_UNLIKELY(l1br->hdr.tn >= ARRAY_SIZE(gprs->pdch))) {
+ LOGP_GPRS(gprs, LOGL_ERROR,
+ "Rx malformed UL BLOCK.req (fn=%u, tn=%u)\n",
+ fn, l1br->hdr.tn);
+ return -EINVAL;
+ }
+
+ pdch = &gprs->pdch[l1br->hdr.tn];
+ data_len = msgb_l1len(msg) - sizeof(*l1br);
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Rx UL BLOCK.req (fn=%u, len=%zu): %s\n",
+ fn, data_len, osmo_hexdump(l1br->data, data_len));
+
+ if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) {
+ LOGP_PDCH(pdch, LOGL_ERROR,
+ "Rx UL BLOCK.req (fn=%u, len=%zu), but this PDCH has no configured TBFs\n",
+ fn, data_len);
+ return -EINVAL;
+ }
+
+ *req = (struct l1gprs_prim_ul_block_req) {
+ .hdr = {
+ .fn = fn,
+ .tn = l1br->hdr.tn,
+ },
+ .data = &l1br->data[0],
+ .data_len = data_len,
+ };
+
+ return 0;
+}
+
+struct msgb *l1gprs_handle_ul_block_cnf(struct l1gprs_state *gprs,
+ uint32_t fn, uint8_t tn,
+ const uint8_t *data,
+ size_t data_len)
+{
+ const struct l1gprs_pdch *pdch = NULL;
+ struct l1ctl_gprs_ul_block_cnf *l1bc;
+ struct msgb *msg;
+
+ OSMO_ASSERT(tn < ARRAY_SIZE(gprs->pdch));
+ pdch = &gprs->pdch[tn];
+
+ LOGP_PDCH(pdch, LOGL_DEBUG, "Rx UL BLOCK.cnf (fn=%u)\n", fn);
+
+ if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) {
+ LOGP_PDCH(pdch, LOGL_ERROR,
+ "Rx UL BLOCK.cnf (fn=%u), but this PDCH has no active TBFs\n",
+ fn);
+ return NULL;
+ }
+
+ msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_UL_BLOCK_CNF);
+ if (OSMO_UNLIKELY(msg == NULL)) {
+ LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n");
+ return NULL;
+ }
+
+ l1bc = (void *)msgb_put(msg, sizeof(*l1bc));
+ *l1bc = (struct l1ctl_gprs_ul_block_cnf) {
+ .fn = htonl(fn),
+ .tn = tn,
+ };
+
+ if (data != NULL && data_len > 0)
+ memcpy(msgb_put(msg, data_len), data, data_len);
+
+ return msg;
+}
+
+/* Check if a Downlink block is a PTCCH/D (see 3GPP TS 45.002, table 6) */
+#define BLOCK_IND_IS_PTCCH(ind) \
+ (((ind)->hdr.fn % 104) == 12)
+
+struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs,
+ const struct l1gprs_prim_dl_block_ind *ind,
+ uint8_t *usf)
+{
+ const struct l1gprs_pdch *pdch = NULL;
+ struct l1ctl_gprs_dl_block_ind *l1bi;
+ enum osmo_gprs_cs cs;
+ struct msgb *msg;
+
+ if (OSMO_UNLIKELY(ind->hdr.tn >= ARRAY_SIZE(gprs->pdch))) {
+ LOGP_GPRS(gprs, LOGL_ERROR,
+ "Rx malformed DL BLOCK.ind (tn=%u)\n",
+ ind->hdr.tn);
+ return NULL;
+ }
+
+ pdch = &gprs->pdch[ind->hdr.tn];
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Rx DL BLOCK.ind (%s, fn=%u, len=%zu): %s\n",
+ BLOCK_IND_IS_PTCCH(ind) ? "PTCCH" : "PDTCH",
+ ind->hdr.fn, ind->data_len, osmo_hexdump(ind->data, ind->data_len));
+
+ l1gprs_check_pending_tbfs(gprs, ind->hdr.fn);
+
+ if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) {
+ if (pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count > 0)
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Rx DL BLOCK.ind (fn=%u), but this PDCH has no active TBFs yet\n",
+ ind->hdr.fn);
+ else
+ LOGP_PDCH(pdch, LOGL_ERROR,
+ "Rx DL BLOCK.ind (fn=%u), but this PDCH has no configured TBFs\n",
+ ind->hdr.fn);
+ return NULL;
+ }
+
+ msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_DL_BLOCK_IND);
+ if (OSMO_UNLIKELY(msg == NULL)) {
+ LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n");
+ return NULL;
+ }
+
+ l1bi = (void *)msgb_put(msg, sizeof(*l1bi));
+ *l1bi = (struct l1ctl_gprs_dl_block_ind) {
+ .hdr = {
+ .fn = htonl(ind->hdr.fn),
+ .tn = ind->hdr.tn,
+ },
+ .meas = {
+ .ber10k = htons(ind->meas.ber10k),
+ .ci_cb = htons(ind->meas.ci_cb),
+ .rx_lev = ind->meas.rx_lev,
+ },
+ .usf = 0xff,
+ };
+
+ if (ind->data_len == 0)
+ return msg;
+ if (BLOCK_IND_IS_PTCCH(ind)) {
+ memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len);
+ return msg;
+ }
+
+ cs = osmo_gprs_dl_cs_by_block_bytes(ind->data_len);
+ switch (cs) {
+ case OSMO_GPRS_CS1:
+ case OSMO_GPRS_CS2:
+ case OSMO_GPRS_CS3:
+ case OSMO_GPRS_CS4:
+ l1bi->usf = ind->data[0] & 0x07;
+ *usf = l1bi->usf;
+ /* Determine whether to include the payload or not */
+ if (l1gprs_pdch_filter_dl_block(pdch, ind->data))
+ memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len);
+ break;
+ case OSMO_GPRS_CS_NONE:
+ LOGP_PDCH(pdch, LOGL_ERROR,
+ "Failed to determine Coding Scheme (len=%zu)\n", ind->data_len);
+ break;
+ default:
+ LOGP_PDCH(pdch, LOGL_NOTICE, "Coding Scheme %d is not supported\n", cs);
+ break;
+ }
+
+ return msg;
+}
+
+struct msgb *l1gprs_handle_rts_ind(struct l1gprs_state *gprs, uint32_t fn, uint8_t tn, uint8_t usf)
+{
+ const struct l1gprs_pdch *pdch = NULL;
+ struct l1ctl_gprs_rts_ind *l1bi;
+ struct msgb *msg;
+
+ OSMO_ASSERT(tn < ARRAY_SIZE(gprs->pdch));
+ pdch = &gprs->pdch[tn];
+
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Rx RTS.ind (PDTCH, fn=%u, usf=%u)\n",
+ fn, usf);
+
+ l1gprs_check_pending_tbfs(gprs, fn);
+
+ if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) {
+ if (pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count > 0)
+ LOGP_PDCH(pdch, LOGL_DEBUG,
+ "Rx RTS.ind (fn=%u, usf=%u), but this PDCH has no active TBFs yet\n",
+ fn, usf);
+ else
+ LOGP_PDCH(pdch, LOGL_ERROR,
+ "Rx RTS.ind (fn=%u, usf=%u), but this PDCH has no configured TBFs\n",
+ fn, usf);
+ return NULL;
+ }
+
+ msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_RTS_IND);
+ if (OSMO_UNLIKELY(msg == NULL)) {
+ LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n");
+ return NULL;
+ }
+
+ l1bi = (void *)msgb_put(msg, sizeof(*l1bi));
+ *l1bi = (struct l1ctl_gprs_rts_ind) {
+ .fn = htonl(fn),
+ .tn = tn,
+ .usf = usf,
+ };
+
+ return msg;
+}
diff --git a/src/shared/libosmocore/include/osmocom/core/bitvec.h b/src/shared/libosmocore/include/osmocom/core/bitvec.h
index 9c000d02..d441c844 100644
--- a/src/shared/libosmocore/include/osmocom/core/bitvec.h
+++ b/src/shared/libosmocore/include/osmocom/core/bitvec.h
@@ -17,10 +17,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.
- *
*/
/*! \defgroup bitvec Bit vectors
diff --git a/src/shared/libosmocore/include/osmocom/core/conv.h b/src/shared/libosmocore/include/osmocom/core/conv.h
index e5b2a975..e1e5c813 100644
--- a/src/shared/libosmocore/include/osmocom/core/conv.h
+++ b/src/shared/libosmocore/include/osmocom/core/conv.h
@@ -14,10 +14,6 @@
* 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.
*/
/*! \defgroup conv Convolutional encoding and decoding routines
diff --git a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
index 89d083ae..9d17e2d1 100644
--- a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
+++ b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl
@@ -14,10 +14,6 @@
* 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.
*/
#ifndef __OSMO_CRCXXGEN_H__
diff --git a/src/shared/libosmocore/include/osmocom/core/crcgen.h b/src/shared/libosmocore/include/osmocom/core/crcgen.h
index 8e208a74..62c45458 100644
--- a/src/shared/libosmocore/include/osmocom/core/crcgen.h
+++ b/src/shared/libosmocore/include/osmocom/core/crcgen.h
@@ -14,10 +14,6 @@
* 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.
*/
#ifndef __OSMO_CRCGEN_H__
diff --git a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
index 079f440d..fccbf7e8 100644
--- a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
+++ b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h
@@ -12,10 +12,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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
linux/include/linux/rbtree.h
To use rbtrees you'll have to implement your own insert and search cores.
diff --git a/src/shared/libosmocore/include/osmocom/core/msgb.h b/src/shared/libosmocore/include/osmocom/core/msgb.h
index a1939ab6..0390a96f 100644
--- a/src/shared/libosmocore/include/osmocom/core/msgb.h
+++ b/src/shared/libosmocore/include/osmocom/core/msgb.h
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/shared/libosmocore/include/osmocom/core/msgfile.h b/src/shared/libosmocore/include/osmocom/core/msgfile.h
index c5e67a45..d4e21f8f 100644
--- a/src/shared/libosmocore/include/osmocom/core/msgfile.h
+++ b/src/shared/libosmocore/include/osmocom/core/msgfile.h
@@ -13,10 +13,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.
- *
*/
#ifndef MSG_FILE_H
diff --git a/src/shared/libosmocore/include/osmocom/core/serial.h b/src/shared/libosmocore/include/osmocom/core/serial.h
index 889bd8a1..8810f332 100644
--- a/src/shared/libosmocore/include/osmocom/core/serial.h
+++ b/src/shared/libosmocore/include/osmocom/core/serial.h
@@ -14,10 +14,6 @@
* 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.
*/
/*! \defgroup serial Utility functions to deal with serial ports
diff --git a/src/shared/libosmocore/include/osmocom/core/timer.h b/src/shared/libosmocore/include/osmocom/core/timer.h
index d37af806..bec2f05a 100644
--- a/src/shared/libosmocore/include/osmocom/core/timer.h
+++ b/src/shared/libosmocore/include/osmocom/core/timer.h
@@ -12,10 +12,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.
- *
*/
/*! \defgroup timer Osmocom timers
diff --git a/src/shared/libosmocore/include/osmocom/core/timer_compat.h b/src/shared/libosmocore/include/osmocom/core/timer_compat.h
index d86c109e..20240873 100644
--- a/src/shared/libosmocore/include/osmocom/core/timer_compat.h
+++ b/src/shared/libosmocore/include/osmocom/core/timer_compat.h
@@ -12,10 +12,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.
- *
*/
/*! \defgroup timer Osmocom timers
diff --git a/src/shared/libosmocore/include/osmocom/core/write_queue.h b/src/shared/libosmocore/include/osmocom/core/write_queue.h
index 816c0364..c4041641 100644
--- a/src/shared/libosmocore/include/osmocom/core/write_queue.h
+++ b/src/shared/libosmocore/include/osmocom/core/write_queue.h
@@ -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.
- *
*/
#ifndef OSMO_WQUEUE_H
#define OSMO_WQUEUE_H
diff --git a/src/shared/libosmocore/include/osmocom/gsm/a5.h b/src/shared/libosmocore/include/osmocom/gsm/a5.h
index 649dbab1..807a2d83 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/a5.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/a5.h
@@ -14,10 +14,6 @@
* 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.
*/
#ifndef __OSMO_A5_H__
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
index 5380dd9e..11c27b7d 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h
@@ -12,10 +12,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.
- *
*/
#ifndef OSMOCORE_GSM0808_H
#define OSMOCORE_GSM0808_H
diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
index 6d316727..9e277d12 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h
@@ -16,10 +16,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.
- *
*/
#ifndef GSM_UTILS_H
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
index 172ef678..e5d129ed 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -345,7 +345,7 @@ enum gsm48_chan_mode {
GSM48_CMODE_DATA_14k5 = 0x0f,
GSM48_CMODE_DATA_12k0 = 0x03,
GSM48_CMODE_DATA_6k0 = 0x0b,
- GSM48_CMODE_DATA_3k6 = 0x23,
+ GSM48_CMODE_DATA_3k6 = 0x13,
};
/* Chapter 9.1.2 */
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
index 57a8f687..41d65af6 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -17,10 +17,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 <stdint.h>
diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
index 694df938..76e8bd07 100644
--- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
+++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h
@@ -17,10 +17,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.
- *
*/
/*! \addtogroup oml
diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
index 3c222014..29e8e13e 100644
--- a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
+++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
@@ -12,10 +12,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.
- *
*/
#ifndef TELNET_INTERFACE_H
diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h
index 22a184d6..f0d269c6 100644
--- a/src/shared/libosmocore/include/osmocom/vty/vector.h
+++ b/src/shared/libosmocore/include/osmocom/vty/vector.h
@@ -13,10 +13,6 @@
* 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 GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
diff --git a/src/shared/libosmocore/src/application.c b/src/shared/libosmocore/src/application.c
index e0d989e5..98175fb8 100644
--- a/src/shared/libosmocore/src/application.c
+++ b/src/shared/libosmocore/src/application.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.
- *
*/
/*! \file application.c
diff --git a/src/shared/libosmocore/src/backtrace.c b/src/shared/libosmocore/src/backtrace.c
index 5b93becb..53cece67 100644
--- a/src/shared/libosmocore/src/backtrace.c
+++ b/src/shared/libosmocore/src/backtrace.c
@@ -14,10 +14,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.
- *
*/
/*! \file backtrace.c
diff --git a/src/shared/libosmocore/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c
index 714c11b7..1e3ee6a0 100644
--- a/src/shared/libosmocore/src/bitvec.c
+++ b/src/shared/libosmocore/src/bitvec.c
@@ -14,10 +14,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.
- *
*/
/*! \addtogroup bitvec
diff --git a/src/shared/libosmocore/src/codec/gsm610.c b/src/shared/libosmocore/src/codec/gsm610.c
index 35f6011d..faa8fb4c 100644
--- a/src/shared/libosmocore/src/codec/gsm610.c
+++ b/src/shared/libosmocore/src/codec/gsm610.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 <stdint.h>
diff --git a/src/shared/libosmocore/src/codec/gsm620.c b/src/shared/libosmocore/src/codec/gsm620.c
index fa570e4f..67e1a8ba 100644
--- a/src/shared/libosmocore/src/codec/gsm620.c
+++ b/src/shared/libosmocore/src/codec/gsm620.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 <stdint.h>
diff --git a/src/shared/libosmocore/src/codec/gsm660.c b/src/shared/libosmocore/src/codec/gsm660.c
index c044a2ab..bd003416 100644
--- a/src/shared/libosmocore/src/codec/gsm660.c
+++ b/src/shared/libosmocore/src/codec/gsm660.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 <stdint.h>
diff --git a/src/shared/libosmocore/src/codec/gsm690.c b/src/shared/libosmocore/src/codec/gsm690.c
index fdf3302f..8d94c5ac 100644
--- a/src/shared/libosmocore/src/codec/gsm690.c
+++ b/src/shared/libosmocore/src/codec/gsm690.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 <stdint.h>
diff --git a/src/shared/libosmocore/src/conv.c b/src/shared/libosmocore/src/conv.c
index ebc3eda7..77c505c1 100644
--- a/src/shared/libosmocore/src/conv.c
+++ b/src/shared/libosmocore/src/conv.c
@@ -16,10 +16,6 @@
* 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.
*/
/*! \addtogroup conv
diff --git a/src/shared/libosmocore/src/crcXXgen.c.tpl b/src/shared/libosmocore/src/crcXXgen.c.tpl
index 80bf1e2a..b4486c55 100644
--- a/src/shared/libosmocore/src/crcXXgen.c.tpl
+++ b/src/shared/libosmocore/src/crcXXgen.c.tpl
@@ -16,10 +16,6 @@
* 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.
*/
/*! \addtogroup crcgen
diff --git a/src/shared/libosmocore/src/gsm/a5.c b/src/shared/libosmocore/src/gsm/a5.c
index 356060ab..6001908c 100644
--- a/src/shared/libosmocore/src/gsm/a5.c
+++ b/src/shared/libosmocore/src/gsm/a5.c
@@ -20,10 +20,6 @@
* 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.
*/
/*! \addtogroup a5
diff --git a/src/shared/libosmocore/src/gsm/auth_comp128v1.c b/src/shared/libosmocore/src/gsm/auth_comp128v1.c
index 41aef71c..14e44f4a 100644
--- a/src/shared/libosmocore/src/gsm/auth_comp128v1.c
+++ b/src/shared/libosmocore/src/gsm/auth_comp128v1.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 <osmocom/crypt/auth.h>
diff --git a/src/shared/libosmocore/src/gsm/auth_core.c b/src/shared/libosmocore/src/gsm/auth_core.c
index 5cf8dfcf..de3d26a7 100644
--- a/src/shared/libosmocore/src/gsm/auth_core.c
+++ b/src/shared/libosmocore/src/gsm/auth_core.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
diff --git a/src/shared/libosmocore/src/gsm/auth_milenage.c b/src/shared/libosmocore/src/gsm/auth_milenage.c
index 5b2787dd..cac1a056 100644
--- a/src/shared/libosmocore/src/gsm/auth_milenage.c
+++ b/src/shared/libosmocore/src/gsm/auth_milenage.c
@@ -14,10 +14,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 <osmocom/crypt/auth.h>
diff --git a/src/shared/libosmocore/src/gsm/comp128.c b/src/shared/libosmocore/src/gsm/comp128.c
index b7a23820..5aaf91f4 100644
--- a/src/shared/libosmocore/src/gsm/comp128.c
+++ b/src/shared/libosmocore/src/gsm/comp128.c
@@ -26,10 +26,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.
- *
*/
/*
diff --git a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
index b9a22a10..db3b0846 100644
--- a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
+++ b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
diff --git a/src/shared/libosmocore/src/gsm/gsm0480.c b/src/shared/libosmocore/src/gsm/gsm0480.c
index b9b3ed97..6a4dd291 100644
--- a/src/shared/libosmocore/src/gsm/gsm0480.c
+++ b/src/shared/libosmocore/src/gsm/gsm0480.c
@@ -16,10 +16,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 <osmocom/gsm/gsm0480.h>
diff --git a/src/shared/libosmocore/src/gsm/gsm0808.c b/src/shared/libosmocore/src/gsm/gsm0808.c
index 30098278..55dc41c2 100644
--- a/src/shared/libosmocore/src/gsm/gsm0808.c
+++ b/src/shared/libosmocore/src/gsm/gsm0808.c
@@ -12,10 +12,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 <osmocom/gsm/gsm0808.h>
diff --git a/src/shared/libosmocore/src/gsm/gsm48.c b/src/shared/libosmocore/src/gsm/gsm48.c
index ea05d450..b1ef65aa 100644
--- a/src/shared/libosmocore/src/gsm/gsm48.c
+++ b/src/shared/libosmocore/src/gsm/gsm48.c
@@ -16,10 +16,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 <stdint.h>
diff --git a/src/shared/libosmocore/src/gsm/gsm48_ie.c b/src/shared/libosmocore/src/gsm/gsm48_ie.c
index 78619b97..84252c6d 100644
--- a/src/shared/libosmocore/src/gsm/gsm48_ie.c
+++ b/src/shared/libosmocore/src/gsm/gsm48_ie.c
@@ -16,10 +16,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.
- *
*/
diff --git a/src/shared/libosmocore/src/gsm/gsm_utils.c b/src/shared/libosmocore/src/gsm/gsm_utils.c
index 8b1fae08..4b170df7 100644
--- a/src/shared/libosmocore/src/gsm/gsm_utils.c
+++ b/src/shared/libosmocore/src/gsm/gsm_utils.c
@@ -16,10 +16,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.
- *
*/
/*! \mainpage libosmogsm Documentation
diff --git a/src/shared/libosmocore/src/gsm/lapd_core.c b/src/shared/libosmocore/src/gsm/lapd_core.c
index 96099edb..47a2a8dd 100644
--- a/src/shared/libosmocore/src/gsm/lapd_core.c
+++ b/src/shared/libosmocore/src/gsm/lapd_core.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.
- *
*/
/*! \addtogroup lapd
diff --git a/src/shared/libosmocore/src/gsm/lapdm.c b/src/shared/libosmocore/src/gsm/lapdm.c
index 1c08113e..5e1c2d54 100644
--- a/src/shared/libosmocore/src/gsm/lapdm.c
+++ b/src/shared/libosmocore/src/gsm/lapdm.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.
- *
*/
/*! \addtogroup lapdm
diff --git a/src/shared/libosmocore/src/gsm/rsl.c b/src/shared/libosmocore/src/gsm/rsl.c
index 5693b4f0..366eae5b 100644
--- a/src/shared/libosmocore/src/gsm/rsl.c
+++ b/src/shared/libosmocore/src/gsm/rsl.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 <stdint.h>
diff --git a/src/shared/libosmocore/src/gsm/rxlev_stat.c b/src/shared/libosmocore/src/gsm/rxlev_stat.c
index d226861e..fb3a2a59 100644
--- a/src/shared/libosmocore/src/gsm/rxlev_stat.c
+++ b/src/shared/libosmocore/src/gsm/rxlev_stat.c
@@ -14,10 +14,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 <unistd.h>
diff --git a/src/shared/libosmocore/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c
index ce722da9..12a1dcf8 100644
--- a/src/shared/libosmocore/src/gsmtap_util.c
+++ b/src/shared/libosmocore/src/gsmtap_util.c
@@ -14,10 +14,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 "../config.h"
diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c
index f58265f7..e187df24 100644
--- a/src/shared/libosmocore/src/logging.c
+++ b/src/shared/libosmocore/src/logging.c
@@ -14,10 +14,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.
- *
*/
/* \addtogroup logging
diff --git a/src/shared/libosmocore/src/logging_syslog.c b/src/shared/libosmocore/src/logging_syslog.c
index 5b0ae5ff..8bc2dfdf 100644
--- a/src/shared/libosmocore/src/logging_syslog.c
+++ b/src/shared/libosmocore/src/logging_syslog.c
@@ -13,10 +13,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.
- *
*/
/*! \addtogroup logging
diff --git a/src/shared/libosmocore/src/msgb.c b/src/shared/libosmocore/src/msgb.c
index c8564dbb..4c703feb 100644
--- a/src/shared/libosmocore/src/msgb.c
+++ b/src/shared/libosmocore/src/msgb.c
@@ -12,10 +12,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.
- *
*/
/*! \addtogroup msgb
diff --git a/src/shared/libosmocore/src/msgfile.c b/src/shared/libosmocore/src/msgfile.c
index d2b180d7..14929ed6 100644
--- a/src/shared/libosmocore/src/msgfile.c
+++ b/src/shared/libosmocore/src/msgfile.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 <osmocom/core/msgfile.h>
diff --git a/src/shared/libosmocore/src/panic.c b/src/shared/libosmocore/src/panic.c
index be644ff1..19134037 100644
--- a/src/shared/libosmocore/src/panic.c
+++ b/src/shared/libosmocore/src/panic.c
@@ -14,10 +14,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.
- *
*/
/*! \addtogroup utils
diff --git a/src/shared/libosmocore/src/plugin.c b/src/shared/libosmocore/src/plugin.c
index 998bca35..9f17698f 100644
--- a/src/shared/libosmocore/src/plugin.c
+++ b/src/shared/libosmocore/src/plugin.c
@@ -14,10 +14,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 "../config.h"
diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c
index 8a232e86..79d41dd8 100644
--- a/src/shared/libosmocore/src/rate_ctr.c
+++ b/src/shared/libosmocore/src/rate_ctr.c
@@ -14,10 +14,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.
- *
*/
/*! \addtogroup rate_ctr
diff --git a/src/shared/libosmocore/src/rbtree.c b/src/shared/libosmocore/src/rbtree.c
index 4e7c0f3a..2da7d7e4 100644
--- a/src/shared/libosmocore/src/rbtree.c
+++ b/src/shared/libosmocore/src/rbtree.c
@@ -13,10 +13,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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
linux/lib/rbtree.c
*/
diff --git a/src/shared/libosmocore/src/select.c b/src/shared/libosmocore/src/select.c
index 6b73377a..11f1a2a5 100644
--- a/src/shared/libosmocore/src/select.c
+++ b/src/shared/libosmocore/src/select.c
@@ -13,10 +13,6 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <fcntl.h>
diff --git a/src/shared/libosmocore/src/serial.c b/src/shared/libosmocore/src/serial.c
index a025ae91..780cf440 100644
--- a/src/shared/libosmocore/src/serial.c
+++ b/src/shared/libosmocore/src/serial.c
@@ -16,10 +16,6 @@
* 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.
*/
/*! \addtogroup serial
diff --git a/src/shared/libosmocore/src/signal.c b/src/shared/libosmocore/src/signal.c
index 63843849..6f8c798e 100644
--- a/src/shared/libosmocore/src/signal.c
+++ b/src/shared/libosmocore/src/signal.c
@@ -12,10 +12,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 <osmocom/core/signal.h>
diff --git a/src/shared/libosmocore/src/statistics.c b/src/shared/libosmocore/src/statistics.c
index e28541ba..d18f4160 100644
--- a/src/shared/libosmocore/src/statistics.c
+++ b/src/shared/libosmocore/src/statistics.c
@@ -14,10 +14,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 <string.h>
diff --git a/src/shared/libosmocore/src/timer.c b/src/shared/libosmocore/src/timer.c
index 6d4abc26..55d08d33 100644
--- a/src/shared/libosmocore/src/timer.c
+++ b/src/shared/libosmocore/src/timer.c
@@ -17,10 +17,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.
- *
*/
/* These store the amount of time that we wait until next timer expires. */
diff --git a/src/shared/libosmocore/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c
index 6029d58b..4f13852b 100644
--- a/src/shared/libosmocore/src/vty/logging_vty.c
+++ b/src/shared/libosmocore/src/vty/logging_vty.c
@@ -13,10 +13,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 <stdlib.h>
diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c
index 1abf141d..821a6498 100644
--- a/src/shared/libosmocore/src/vty/telnet_interface.c
+++ b/src/shared/libosmocore/src/vty/telnet_interface.c
@@ -12,10 +12,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 <sys/socket.h>
diff --git a/src/shared/libosmocore/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c
index e9c0d2d7..af49c308 100644
--- a/src/shared/libosmocore/src/vty/utils.c
+++ b/src/shared/libosmocore/src/vty/utils.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/shared/libosmocore/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c
index 4012f24b..bda96d9c 100644
--- a/src/shared/libosmocore/src/vty/vector.c
+++ b/src/shared/libosmocore/src/vty/vector.c
@@ -12,10 +12,6 @@
* 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 GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
diff --git a/src/shared/libosmocore/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c
index cef40f83..d3532102 100644
--- a/src/shared/libosmocore/src/write_queue.c
+++ b/src/shared/libosmocore/src/write_queue.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 <osmocom/core/write_queue.h>
diff --git a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c
index 077063be..e31d0561 100644
--- a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c
+++ b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c
@@ -12,10 +12,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 <string.h>
diff --git a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
index 7e5e97b5..8cc0fbbd 100644
--- a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
+++ b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c
@@ -12,10 +12,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 <osmocom/gsm/gsm0808.h>
diff --git a/src/shared/libosmocore/tests/lapd/lapd_test.c b/src/shared/libosmocore/tests/lapd/lapd_test.c
index d58bec65..0bd89b79 100644
--- a/src/shared/libosmocore/tests/lapd/lapd_test.c
+++ b/src/shared/libosmocore/tests/lapd/lapd_test.c
@@ -13,10 +13,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 <osmocom/core/logging.h>
diff --git a/src/shared/libosmocore/tests/msgfile/msgfile_test.c b/src/shared/libosmocore/tests/msgfile/msgfile_test.c
index ed7aa978..abea9173 100644
--- a/src/shared/libosmocore/tests/msgfile/msgfile_test.c
+++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.c
@@ -13,10 +13,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 <osmocom/core/msgfile.h>
diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c
index 6df4b623..18a7d1ec 100644
--- a/src/shared/libosmocore/tests/sms/sms_test.c
+++ b/src/shared/libosmocore/tests/sms/sms_test.c
@@ -13,10 +13,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>
diff --git a/src/shared/libosmocore/tests/smscb/smscb_test.c b/src/shared/libosmocore/tests/smscb/smscb_test.c
index e10e12d8..34178e28 100644
--- a/src/shared/libosmocore/tests/smscb/smscb_test.c
+++ b/src/shared/libosmocore/tests/smscb/smscb_test.c
@@ -12,10 +12,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 <osmocom/gsm/protocol/gsm_03_41.h>
diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c
index ba3127d4..7b973c06 100644
--- a/src/shared/libosmocore/tests/timer/timer_test.c
+++ b/src/shared/libosmocore/tests/timer/timer_test.c
@@ -16,10 +16,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>
diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.c b/src/shared/libosmocore/tests/ussd/ussd_test.c
index 55384f10..0ddab90f 100644
--- a/src/shared/libosmocore/tests/ussd/ussd_test.c
+++ b/src/shared/libosmocore/tests/ussd/ussd_test.c
@@ -13,10 +13,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 <osmocom/core/application.h>
diff --git a/src/shared/libosmocore/utils/osmo-arfcn.c b/src/shared/libosmocore/utils/osmo-arfcn.c
index 15adbca2..172ee54e 100644
--- a/src/shared/libosmocore/utils/osmo-arfcn.c
+++ b/src/shared/libosmocore/utils/osmo-arfcn.c
@@ -14,10 +14,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>
diff --git a/src/shared/libosmocore/utils/osmo-auc-gen.c b/src/shared/libosmocore/utils/osmo-auc-gen.c
index 7a3c124c..449bcc6f 100644
--- a/src/shared/libosmocore/utils/osmo-auc-gen.c
+++ b/src/shared/libosmocore/utils/osmo-auc-gen.c
@@ -14,10 +14,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.
- *
*/
diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh
index 69dfbe11..aec273fa 100755
--- a/src/shared/update-libosmocore.sh
+++ b/src/shared/update-libosmocore.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-(cd ../.. && git subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master)
+(cd ../.. && git subtree pull --prefix=src/shared/libosmocore https://gitea.osmocom.org/osmocom/libosmocore master)
diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile
index 104ddac9..2a376bc3 100644
--- a/src/target/firmware/Makefile
+++ b/src/target/firmware/Makefile
@@ -24,7 +24,8 @@ ENV_e88flash_OBJS=board/compal/start.rom.o board/compal/header.o board/compal/ex
#
# List of all supported boards (meant to be overridden on command line)
-BOARDS?=compal_e88 compal_e86 compal_e99 se_j100 gta0x gtm900b fcdev3b pirelli_dpl10
+BOARDS?=compal_e88 compal_e86 compal_e99 se_j100 se_k2x0 gta0x gtm900b fcdev3b \
+ pirelli_dpl10 tr800
# Framebuffer support, board specific drivers
FB_OBJS=fb/framebuffer.o fb/font.o fb/helvR08.o fb/helvB14.o fb/c64.o \
@@ -35,6 +36,7 @@ FB_e99_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_ssd1783.o
FB_e86_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_td014.o
FB_j100_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_ssd1963.o
FB_dpl10_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_s6b33b1x.o
+FB_k2x0_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_k2x0.o
FB_dummy_OBJS=$(FB_OBJS) fb/fb_dummy.o
# TI Calypso
@@ -62,6 +64,13 @@ BOARD_fcdev3b_OBJS=$(calypso_COMMON_OBJS) board/fcdev3b/init.o \
board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS)
BOARD_fcdev3b_ENVIRONMENTS=highram
+# iWOW TR-800 aka FreeCalypso Tango
+BOARD_tr800_OBJS=$(calypso_COMMON_OBJS) board/tr800/init.o \
+ board/tr800/rffe_leo_quadband.o board/gta0x/rf_tables.o \
+ board/tr800/afcparams.o \
+ board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS)
+BOARD_tr800_ENVIRONMENTS=highram
+
# Pirelli DP-L10
BOARD_pirelli_dpl10_OBJS=$(calypso_COMMON_OBJS) board/pirelli_dpl10/init.o \
board/pirelli_dpl10/rffe_dpl10_triband.o \
@@ -100,6 +109,12 @@ BOARD_se_j100_OBJS=$(compal_COMMON_OBJS) board/se_j100/init.o \
battery/dummy.o $(FB_j100_OBJS)
BOARD_se_j100_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS)
+# Sony Ericsson K200i/K220i
+BOARD_se_k2x0_OBJS=$(calypso_COMMON_OBJS) board/se_k2x0/init.o \
+ board/se_k2x0/rffe_k2x0.o \
+ board/gta0x/rf_tables.o board/gta0x/afcparams.o \
+ board/common/readcal_tiffs.o battery/dummy.o $(FB_k2x0_OBJS)
+BOARD_se_k2x0_ENVIRONMENTS=highram
#
# Applications
@@ -126,8 +141,7 @@ ANY_APP_LIBS+= calypso/libcalypso.a \
comm/libcomm.a \
tiffs/libtiffs.a \
../../shared/libosmocore/build-target/src/.libs/libosmocore.a \
- ../../shared/libosmocore/build-target/src/gsm/.libs/libosmogsm.a \
- ../../shared/libosmocore/build-target/src/codec/.libs/libosmocodec.a
+ ../../shared/libosmocore/build-target/src/gsm/.libs/libosmogsm.a
#
diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc
index 2be240d2..86e8f3ce 100644
--- a/src/target/firmware/Makefile.inc
+++ b/src/target/firmware/Makefile.inc
@@ -4,7 +4,7 @@
CROSS_COMPILE?=arm-none-eabi-
CC=gcc
-LD=ld
+LD=gcc
AR=ar
SIZE=size
OBJCOPY=objcopy
@@ -22,7 +22,7 @@ CFLAGS += -g$(DEBUGF)
#ASFLAGS=--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__
ASFLAGS=$(INCLUDES) -D__ASSEMBLY__
-LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections --cref
+LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs -Wl,--gc-sections -Wl,--cref
#### QUIET OUTPUT ####
@@ -99,8 +99,8 @@ $(1)_$(2)_$(3)_OBJS+=board/$(2)/$(1).$(3).manifest.o
# define compilation rule, also generates map file
board/$(2)/$(1).$(3).elf board/$(2)/$(1).$(3).map: $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) $$(ENV_$(3)_LDS)
$$(Q_LD)$(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $$(ENV_$(3)_LDS) -Bstatic \
- -Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \
- --start-group $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) --end-group
+ -Wl,-Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \
+ -Wl,--start-group $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) -Wl,--end-group
# define size rule
board/$(2)/$(1).$(3).size: board/$(2)/$(1).$(3).elf
diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c
index 5b792d68..4f495bc8 100644
--- a/src/target/firmware/abb/twl3025.c
+++ b/src/target/firmware/abb/twl3025.c
@@ -14,10 +14,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 <stdint.h>
@@ -377,6 +373,7 @@ void twl3025_unit_enable(enum twl3025_unit unit, int on)
togbr1 = (1 << 5);
else
togbr1 = (1 << 4);
+ break;
case TWL3025_UNIT_VDL:
if (on)
togbr1 = (1 << 3);
diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c
index 5e1ab68d..e5521c3d 100644
--- a/src/target/firmware/apps/compal_dsp_dump/main.c
+++ b/src/target/firmware/apps/compal_dsp_dump/main.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 <memory.h>
diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c
index 481cf170..ecacc183 100644
--- a/src/target/firmware/apps/hello_world/main.c
+++ b/src/target/firmware/apps/hello_world/main.c
@@ -14,10 +14,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 <stdint.h>
@@ -169,8 +165,6 @@ int main(void)
void key_handler(enum key_codes code, enum key_states state)
{
- char test[16];
-
if (state != PRESSED)
return;
diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c
index b2e66e2c..81354686 100644
--- a/src/target/firmware/apps/layer1/main.c
+++ b/src/target/firmware/apps/layer1/main.c
@@ -14,10 +14,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 <stdint.h>
@@ -61,7 +57,6 @@ static void key_handler(enum key_codes code, enum key_states state);
int main(void)
{
uint8_t atr[20];
- uint8_t atrLength = 0;
board_init(1);
@@ -105,7 +100,7 @@ int main(void)
puts("Power up simcard:\n");
memset(atr,0,sizeof(atr));
- atrLength = calypso_sim_powerup(atr);
+ calypso_sim_powerup(atr);
read_factory_rf_calibration();
layer1_init();
diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c
index 9b7a1b59..e828fffd 100644
--- a/src/target/firmware/apps/loader/main.c
+++ b/src/target/firmware/apps/loader/main.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/apps/loader_mtk/main.c b/src/target/firmware/apps/loader_mtk/main.c
index f2ebbea1..995d277d 100644
--- a/src/target/firmware/apps/loader_mtk/main.c
+++ b/src/target/firmware/apps/loader_mtk/main.c
@@ -16,10 +16,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 <stdint.h>
diff --git a/src/target/firmware/apps/menu/main.c b/src/target/firmware/apps/menu/main.c
index fa369681..5d96cc0a 100644
--- a/src/target/firmware/apps/menu/main.c
+++ b/src/target/firmware/apps/menu/main.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/apps/rssi/main.c b/src/target/firmware/apps/rssi/main.c
index b04fe28d..29c5430e 100644
--- a/src/target/firmware/apps/rssi/main.c
+++ b/src/target/firmware/apps/rssi/main.c
@@ -14,10 +14,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 <stdint.h>
@@ -920,12 +916,9 @@ static void handle_pm(void)
a = arfcn;
if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
a |= ARFCN_PCS;
- if (uplink)
- a |= ARFCN_UPLINK;
e = a;
pm_mode = PM_SENT;
- }
- if (mode == MODE_SPECTRUM) {
+ } else { /* mode == MODE_SPECTRUM */
if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
a = PCS_MIN | ARFCN_PCS;
e = PCS_MAX | ARFCN_PCS;
diff --git a/src/target/firmware/apps/simtest/main.c b/src/target/firmware/apps/simtest/main.c
index e20c52a1..d41891df 100755
--- a/src/target/firmware/apps/simtest/main.c
+++ b/src/target/firmware/apps/simtest/main.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/apps/snake_game/main.c b/src/target/firmware/apps/snake_game/main.c
index 44dda9af..4945b6f7 100644
--- a/src/target/firmware/apps/snake_game/main.c
+++ b/src/target/firmware/apps/snake_game/main.c
@@ -14,10 +14,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>
diff --git a/src/target/firmware/battery/compal_e88.c b/src/target/firmware/battery/compal_e88.c
index 609d4063..788d8ab2 100644
--- a/src/target/firmware/battery/compal_e88.c
+++ b/src/target/firmware/battery/compal_e88.c
@@ -14,10 +14,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.
- *
*/
/*
@@ -356,7 +352,6 @@ bat_compal_e88_adc_read(){
static void
battery_compal_e88_timer_cb(void *p){
struct osmo_timer_list *tmr = (struct osmo_timer_list*)p;
- int i;
if(bat_compal_e88_adc_read()){ /* read back ADCs after a brief delay */
osmo_timer_schedule(tmr,ADC_TIMER_DELAY);
diff --git a/src/target/firmware/board/common/readcal_tiffs.c b/src/target/firmware/board/common/readcal_tiffs.c
index 43519710..175f25a7 100644
--- a/src/target/firmware/board/common/readcal_tiffs.c
+++ b/src/target/firmware/board/common/readcal_tiffs.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>
diff --git a/src/target/firmware/board/common/tx_calchan.c b/src/target/firmware/board/common/tx_calchan.c
index 9901e712..b86464d6 100644
--- a/src/target/firmware/board/common/tx_calchan.c
+++ b/src/target/firmware/board/common/tx_calchan.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 <stdint.h>
diff --git a/src/target/firmware/board/compal/readcal_common.c b/src/target/firmware/board/compal/readcal_common.c
index 2f7944ce..c0260b67 100644
--- a/src/target/firmware/board/compal/readcal_common.c
+++ b/src/target/firmware/board/compal/readcal_common.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>
diff --git a/src/target/firmware/board/compal/readcal_small.c b/src/target/firmware/board/compal/readcal_small.c
index a036e111..32302d20 100644
--- a/src/target/firmware/board/compal/readcal_small.c
+++ b/src/target/firmware/board/compal/readcal_small.c
@@ -13,10 +13,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 <stdint.h>
diff --git a/src/target/firmware/board/compal/rf_tables.c b/src/target/firmware/board/compal/rf_tables.c
index dd7411da..f115f29a 100644
--- a/src/target/firmware/board/compal/rf_tables.c
+++ b/src/target/firmware/board/compal/rf_tables.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 <stdint.h>
diff --git a/src/target/firmware/board/compal_e86/init.c b/src/target/firmware/board/compal_e86/init.c
index 725f1841..82a96ebd 100644
--- a/src/target/firmware/board/compal_e86/init.c
+++ b/src/target/firmware/board/compal_e86/init.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 <stdint.h>
diff --git a/src/target/firmware/board/compal_e86/tx_ramps.c b/src/target/firmware/board/compal_e86/tx_ramps.c
index 7bac4d51..195242a1 100644
--- a/src/target/firmware/board/compal_e86/tx_ramps.c
+++ b/src/target/firmware/board/compal_e86/tx_ramps.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 <stdint.h>
diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c
index 956f7dc5..09376448 100755
--- a/src/target/firmware/board/compal_e88/init.c
+++ b/src/target/firmware/board/compal_e88/init.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/board/compal_e88/tx_ramps.c b/src/target/firmware/board/compal_e88/tx_ramps.c
index 846a21d4..79b3180d 100644
--- a/src/target/firmware/board/compal_e88/tx_ramps.c
+++ b/src/target/firmware/board/compal_e88/tx_ramps.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 <stdint.h>
diff --git a/src/target/firmware/board/compal_e99/init.c b/src/target/firmware/board/compal_e99/init.c
index 0271f16e..18c94fd1 100644
--- a/src/target/firmware/board/compal_e99/init.c
+++ b/src/target/firmware/board/compal_e99/init.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 <stdint.h>
diff --git a/src/target/firmware/board/compal_e99/readcal.c b/src/target/firmware/board/compal_e99/readcal.c
index c75e1fa5..c4a113fd 100644
--- a/src/target/firmware/board/compal_e99/readcal.c
+++ b/src/target/firmware/board/compal_e99/readcal.c
@@ -13,10 +13,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 <stdint.h>
diff --git a/src/target/firmware/board/fcdev3b/init.c b/src/target/firmware/board/fcdev3b/init.c
index c0705364..03bd6d73 100644
--- a/src/target/firmware/board/fcdev3b/init.c
+++ b/src/target/firmware/board/fcdev3b/init.c
@@ -16,10 +16,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 <stdint.h>
diff --git a/src/target/firmware/board/gta0x/afcparams.c b/src/target/firmware/board/gta0x/afcparams.c
index ba48360d..5bf68acf 100644
--- a/src/target/firmware/board/gta0x/afcparams.c
+++ b/src/target/firmware/board/gta0x/afcparams.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 <stdint.h>
diff --git a/src/target/firmware/board/gta0x/init.c b/src/target/firmware/board/gta0x/init.c
index 7fba7561..49279d18 100644
--- a/src/target/firmware/board/gta0x/init.c
+++ b/src/target/firmware/board/gta0x/init.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/board/gta0x/rf_tables.c b/src/target/firmware/board/gta0x/rf_tables.c
index 9a08b3c7..650c77c7 100644
--- a/src/target/firmware/board/gta0x/rf_tables.c
+++ b/src/target/firmware/board/gta0x/rf_tables.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 <stdint.h>
diff --git a/src/target/firmware/board/gtm900b/afcparams.c b/src/target/firmware/board/gtm900b/afcparams.c
index ca8908dc..9dbbe7e9 100644
--- a/src/target/firmware/board/gtm900b/afcparams.c
+++ b/src/target/firmware/board/gtm900b/afcparams.c
@@ -13,10 +13,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 <stdint.h>
diff --git a/src/target/firmware/board/gtm900b/init.c b/src/target/firmware/board/gtm900b/init.c
index 934e96e8..58a1e1e2 100644
--- a/src/target/firmware/board/gtm900b/init.c
+++ b/src/target/firmware/board/gtm900b/init.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 <stdint.h>
diff --git a/src/target/firmware/board/mediatek/uart.c b/src/target/firmware/board/mediatek/uart.c
index f9e3283b..7b4c49af 100644
--- a/src/target/firmware/board/mediatek/uart.c
+++ b/src/target/firmware/board/mediatek/uart.c
@@ -19,10 +19,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 <debug.h>
diff --git a/src/target/firmware/board/mt62xx/init.c b/src/target/firmware/board/mt62xx/init.c
index dae38cf4..baa6fcd4 100644
--- a/src/target/firmware/board/mt62xx/init.c
+++ b/src/target/firmware/board/mt62xx/init.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/board/pirelli_dpl10/init.c b/src/target/firmware/board/pirelli_dpl10/init.c
index 1af6e7c9..273a11dd 100644
--- a/src/target/firmware/board/pirelli_dpl10/init.c
+++ b/src/target/firmware/board/pirelli_dpl10/init.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 <stdint.h>
diff --git a/src/target/firmware/board/pirelli_dpl10/readcal.c b/src/target/firmware/board/pirelli_dpl10/readcal.c
index 6a4670a0..40f608f8 100644
--- a/src/target/firmware/board/pirelli_dpl10/readcal.c
+++ b/src/target/firmware/board/pirelli_dpl10/readcal.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>
diff --git a/src/target/firmware/board/pirelli_dpl10/rf_tables.c b/src/target/firmware/board/pirelli_dpl10/rf_tables.c
index 34fbbcbc..a2d26b1b 100644
--- a/src/target/firmware/board/pirelli_dpl10/rf_tables.c
+++ b/src/target/firmware/board/pirelli_dpl10/rf_tables.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 <stdint.h>
diff --git a/src/target/firmware/board/se_j100/init.c b/src/target/firmware/board/se_j100/init.c
index 0ae477a4..6352b369 100644
--- a/src/target/firmware/board/se_j100/init.c
+++ b/src/target/firmware/board/se_j100/init.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 <stdint.h>
diff --git a/src/target/firmware/board/se_j100/tx_ramps.c b/src/target/firmware/board/se_j100/tx_ramps.c
index b1110e06..387febb9 100644
--- a/src/target/firmware/board/se_j100/tx_ramps.c
+++ b/src/target/firmware/board/se_j100/tx_ramps.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 <stdint.h>
diff --git a/src/target/firmware/board/se_k2x0/init.c b/src/target/firmware/board/se_k2x0/init.c
new file mode 100644
index 00000000..71218670
--- /dev/null
+++ b/src/target/firmware/board/se_k2x0/init.c
@@ -0,0 +1,140 @@
+/* Initialization for the Sony Ericsson K200i/K220i */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-22 by Steve Markgraf <steve@steve-m.de>
+ *
+ * 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
+ * (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.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <ctors.h>
+#include <memory.h>
+#include <board.h>
+#include <keypad.h>
+#include <console.h>
+#include <flash/cfi_flash.h>
+#include <tiffs.h>
+
+#include <calypso/irq.h>
+#include <calypso/clock.h>
+#include <calypso/dma.h>
+#include <calypso/rtc.h>
+#include <calypso/timer.h>
+#include <uart.h>
+#include <calypso/backlight.h>
+
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+
+#include <fb/framebuffer.h>
+#include "keymap.h"
+
+#define ASIC_CONF_REG 0xfffef008
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define IO_CNTL_REG 0xfffe4804
+#define IO_CONF_REG 0xfffef00a
+
+static void board_io_init(void)
+{
+ uint16_t reg;
+
+ reg = readw(ASIC_CONF_REG);
+ /* Set LPG and PWL pin mux */
+ reg |= (1 << 6) | (1 << 4);
+ /* TWL3025: Set SPI+RIF RX clock to rising edge */
+ reg |= (1 << 13) | (1 << 14);
+ writew(reg, ASIC_CONF_REG);
+
+ writew(0xc060, IO_CNTL_REG);
+ writew(0x03fd, IO_CONF_REG);
+
+ /* set default IO state */
+ writew(0x1f83, ARMIO_LATCH_OUT);
+}
+
+void board_init(int with_irq)
+{
+ /* Configure the memory interface */
+ calypso_mem_cfg(CALYPSO_nCS0, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 5, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS2, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_CS4, 5, CALYPSO_MEM_8bit, 1); /* TODO: add one dummy cycle */
+ calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0);
+
+ /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */
+ calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2);
+
+ /* Configure the RHEA bridge with some sane default values */
+ calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0);
+
+ /* Initialize board-specific GPIO */
+ board_io_init();
+
+ /* Enable bootrom mapping to route exception vectors to RAM */
+ calypso_bootrom(with_irq);
+ calypso_exceptions_install();
+
+ /* Initialize interrupt controller */
+ if (with_irq)
+ irq_init();
+
+ sercomm_bind_uart(UART_MODEM);
+ cons_bind_uart(UART_IRDA);
+
+ /* initialize MODEM UART to be used for sercomm */
+ uart_init(UART_MODEM, with_irq);
+ uart_baudrate(UART_MODEM, UART_115200);
+
+ /* initialize IRDA UART to be used for old-school console code.
+ * note: IRDA uart only accessible on C115 and C117 PCB */
+ uart_init(UART_IRDA, with_irq);
+ uart_baudrate(UART_IRDA, UART_115200);
+
+ /* Initialize hardware timers */
+ hwtimer_init();
+
+ /* Initialize DMA controller */
+ dma_init();
+
+ /* Initialize real time clock */
+ rtc_init();
+
+ /* Initialize system timers (uses hwtimer 2) */
+ timer_init();
+
+ /* Initialize LCD driver and backlight */
+ fb_init();
+ bl_mode_pwl(1);
+ bl_level(50);
+
+ /* Initialize keypad driver */
+ keypad_init(keymap, with_irq);
+
+ /* Initialize ABB driver (uses SPI) */
+ twl3025_init();
+
+ /* K200i uses 13 sectors of 256 KiB each */
+ if (tiffs_init(0x01800000, 0x40000, 13) < 0) {
+ /* K220i uses 52 sectors of 64 KiB each */
+ tiffs_init(0x01800000, 0x10000, 52);
+ }
+}
diff --git a/src/target/firmware/board/se_k2x0/keymap.h b/src/target/firmware/board/se_k2x0/keymap.h
new file mode 100644
index 00000000..aa2d8a56
--- /dev/null
+++ b/src/target/firmware/board/se_k2x0/keymap.h
@@ -0,0 +1,28 @@
+/* Keymap for SE K200i/K220i */
+static const uint8_t keymap[] = {
+ [KEY_0] = 9,
+ [KEY_1] = 1,
+ [KEY_2] = 6,
+ [KEY_3] = 11,
+ [KEY_4] = 2,
+ [KEY_5] = 7,
+ [KEY_6] = 12,
+ [KEY_7] = 3,
+ [KEY_8] = 8,
+ [KEY_9] = 13,
+ [KEY_STAR] = 4,
+ [KEY_HASH] = 14,
+ [KEY_MENU] = 21, /* not existent */
+ [KEY_LEFT_SB] = 0,
+ [KEY_RIGHT_SB] = 5,
+ [KEY_UP] = 16,
+ [KEY_DOWN] = 15,
+ [KEY_LEFT] = 17,
+ [KEY_RIGHT] = 18,
+ [KEY_OK] = 10,
+/* power button is not connected to keypad scan matrix but to TWL3025 */
+ [KEY_POWER] = 31,
+ [KEY_MINUS] = 22, /* not existent */
+ [KEY_PLUS] = 23, /* not existent */
+ [KEY_CAMERA] = 24, /* not existent */
+};
diff --git a/src/target/firmware/board/se_k2x0/rffe_k2x0.c b/src/target/firmware/board/se_k2x0/rffe_k2x0.c
new file mode 100644
index 00000000..f9965acd
--- /dev/null
+++ b/src/target/firmware/board/se_k2x0/rffe_k2x0.c
@@ -0,0 +1,122 @@
+/* RF frontend driver for Sony Ericsson K200i/K220i */
+
+/* (C) 2022 by Steve Markgraf <steve@steve-m.de>
+ *
+ * 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
+ * (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/>.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rffe.h>
+#include <calypso/tsp.h>
+#include <rf/trf6151.h>
+
+/* This is a value that has been measured on the C123 by Harald: 71dBm,
+ it is the difference between the input level at the antenna and what
+ the DSP reports, subtracted by the total gain of the TRF6151 */
+#define SYSTEM_INHERENT_GAIN 71
+
+/* describe how the RF frontend is wired on the SE K200i/K220i */
+#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151, correct for K200i */
+#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */
+#define DCS_TXEN TSPACT(3) /* DCS (as opposed to GSM) Transmit */
+
+#define ASM_VC1 TSPACT(1) /* GSM1800 TX at antenna switch, inverted */
+#define ASM_VC2 TSPACT(2) /* GSM900 TX at antenna switch, inverted */
+
+#define IOTA_STROBE TSPEN(1) /* Strobe for the Iota TSP */
+#define RITA_STROBE TSPEN(2) /* Strobe for the Rita TSP */
+
+/* switch RF Frontend Mode */
+void rffe_mode(enum gsm_band band, int tx)
+{
+ uint16_t tspact = tsp_act_state();
+
+ /* First we mask off all bits from the state cache */
+ tspact &= ~(PA_ENABLE | DCS_TXEN);
+ tspact |= ASM_VC1 | ASM_VC2;
+
+#ifdef CONFIG_TX_ENABLE
+ /* Then we selectively set the bits on, if required */
+ if (tx) {
+ if ((band == GSM_BAND_1800) || band == (GSM_BAND_1900)) {
+ tspact |= DCS_TXEN;
+ tspact &= ~ASM_VC1;
+ } else
+ tspact &= ~ASM_VC2;
+
+ tspact |= PA_ENABLE;
+ }
+#endif /* TRANSMIT_SUPPORT */
+
+ tsp_act_update(tspact);
+}
+
+uint32_t rffe_get_rx_ports(void)
+{
+ return (1 << PORT_LO) | (1 << PORT_DCS1800) | (1 << PORT_PCS1900);
+}
+
+uint32_t rffe_get_tx_ports(void)
+{
+ return (1 << PORT_LO) | (1 << PORT_HI);
+}
+
+/* Returns need for IQ swap */
+int rffe_iq_swapped(uint16_t band_arfcn, int tx)
+{
+ return trf6151_iq_swapped(band_arfcn, tx);
+}
+
+#define MCU_SW_TRACE 0xfffef00e
+#define ARM_CONF_REG 0xfffef006
+#define ASIC_CONF_REG 0xfffef008
+
+void rffe_init(void)
+{
+ /* Configure the TSPEN which is connected to the TWL3025 */
+ tsp_setup(IOTA_STROBE, 1, 0, 0);
+
+ trf6151_init(RITA_STROBE, RITA_RESET);
+}
+
+uint8_t rffe_get_gain(void)
+{
+ return trf6151_get_gain();
+}
+
+void rffe_set_gain(uint8_t dbm)
+{
+ trf6151_set_gain(dbm);
+}
+
+const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN;
+
+/* Given the expected input level of exp_inp dBm/8 and the target of target_bb
+ * dBm8, configure the RF Frontend with the respective gain */
+void rffe_compute_gain(int16_t exp_inp, int16_t target_bb)
+{
+ trf6151_compute_gain(exp_inp, target_bb);
+}
+
+void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb)
+{
+ /* FIXME */
+}
diff --git a/src/target/firmware/board/tr800/afcparams.c b/src/target/firmware/board/tr800/afcparams.c
new file mode 100644
index 00000000..ccbb3608
--- /dev/null
+++ b/src/target/firmware/board/tr800/afcparams.c
@@ -0,0 +1,52 @@
+/*
+ * This code was written by Mychaela Falconia <falcon@freecalypso.org>
+ * who refuses to claim copyright on it and has released it as public domain
+ * instead. NO rights reserved, all rights relinquished.
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+#include <rf/vcxocal.h>
+
+/*
+ * Here is a representative set of AFC Psi parameters that has been
+ * calibrated by iWOW's factory on a TR-800 module, as recorded
+ * in the /gsm/rf/afcparams file:
+ *
+ * Psi_sta_inv: 4387
+ * Psi_st: 12
+ * Psi_st_32: 783154
+ * Psi_st_inv: 5484
+ *
+ * The following AFC slope number is the closest OsmocomBB-style afc_slope
+ * integer corresponding to these Psi numbers; the true value is somewhere
+ * between 358 and 359.
+ *
+ * Please note that all AFC parameters (both Psi and linear) have been
+ * calibrated per unit by iWOW's factory, and they do differ from unit
+ * to unit. Both iWOW and FreeCalypso firmwares make direct use of
+ * per-unit calibrated numbers, but OsmocomBB architecture cannot make
+ * use of them - hence AFC performance with OBB may be significantly
+ * poorer than with either iWOW or FC firmware. The present code has
+ * been contributed by Mother Mychaela solely as a harm reduction measure,
+ * and does NOT constitute any kind of approved production solution -
+ * you've been warned!
+ */
+int16_t afc_slope = 358;
+
+/*
+ * The compiled-in AFC initial DAC value below is the same as was used by
+ * the old OsmocomBB code written for Mot C1xx phones, but it will normally
+ * be overridden by the per-unit factory calibration value read from the
+ * /gsm/rf/afcdac file in FFS.
+ */
+int16_t afc_initial_dac_value = -700;
diff --git a/src/target/firmware/board/tr800/init.c b/src/target/firmware/board/tr800/init.c
new file mode 100644
index 00000000..a8f3253f
--- /dev/null
+++ b/src/target/firmware/board/tr800/init.c
@@ -0,0 +1,257 @@
+/* Initialization for the iWOW TR-800 modem */
+
+/*
+ * This code was written by Mychaela Falconia <falcon@freecalypso.org>
+ * who refuses to claim copyright on it and has released it as public domain
+ * instead. NO rights reserved, all rights relinquished.
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <ctors.h>
+#include <memory.h>
+#include <board.h>
+#include <keypad.h>
+#include <console.h>
+#include <flash/cfi_flash.h>
+#include <tiffs.h>
+
+#include <calypso/irq.h>
+#include <calypso/clock.h>
+#include <calypso/dma.h>
+#include <calypso/rtc.h>
+#include <calypso/timer.h>
+#include <uart.h>
+
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include "keymap.h"
+
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define IO_CNTL_REG 0xfffe4804
+#define ARM_CONF_REG 0xfffef006
+#define ASIC_CONF_REG 0xfffef008
+#define IO_CONF_REG 0xfffef00a
+
+static void board_io_init(void)
+{
+ uint16_t reg;
+
+ reg = readw(ASIC_CONF_REG);
+ /* DSR_MODEM/LPG pin is unconnected - make it LPG dummy output */
+ reg |= (1 << 6);
+ /* TWL3025: Set SPI+RIF RX clock to rising edge */
+ reg |= (1 << 13) | (1 << 14);
+ writew(reg, ASIC_CONF_REG);
+
+ /*
+ * Calypso signals GPIO0, TSPDI/GPIO4, BCLKX/GPIO6, MCUEN1/GPIO8 and
+ * MCUEN2/GPIO13 are unused and unconnected inside the TR-800 module.
+ * Configure them as dummy outputs in order to prevent floating inputs.
+ */
+ writew(0x0215, IO_CONF_REG);
+ writew(0xDC0E, IO_CNTL_REG);
+ writew(0x0000, ARMIO_LATCH_OUT);
+
+ /* configure ADD(22), needed for second half of flash */
+ reg = readw(ARM_CONF_REG);
+ reg |= (1 << 3);
+ writew(reg, ARM_CONF_REG);
+}
+
+/*
+ * A total of 8 Calypso GPIO/multifunction pins (3 pure GPIO, 5 multifunction)
+ * are brought out on the TR-800 module, with module users (application board
+ * designers) explicitly allowed to wire them in whichever way is needed for
+ * the custom application at hand. 6 of these pins power up as inputs.
+ * Should the firmware leave them as inputs, or switch them to dummy outputs
+ * to prevent floating inputs? The answer in FreeCalypso (for TR-800 modules
+ * rebranded as FC Tango) is a special file written into FFS: /etc/tango-pinmux.
+ * This board wiring config file tells the firmware what it should do with each
+ * of the 8 GPIO/multifunction pins in question; the format is defined here:
+ *
+ * https://www.freecalypso.org/hg/freecalypso-docs/file/tip/Tango-pinmux
+ *
+ * The following function reads /etc/tango-pinmux from FFS and applies the pin
+ * multiplexing configuration encoded therein. If this file is missing, all
+ * pins in question are left in their default power-up state.
+ */
+static void board_pinmux_init(void)
+{
+ uint8_t pinmux[4];
+ int rc;
+ uint16_t conf_reg, cntl_reg, out_reg;
+
+ rc = tiffs_read_file_fixedlen("/etc/tango-pinmux", pinmux, 4);
+ if (rc < 0)
+ return; /* error msg already printed */
+ if (rc == 0) {
+ puts("Warning: /etc/tango-pinmux not found, pins left in default power-up state\n");
+ return;
+ }
+ /* read-modify-write registers */
+ conf_reg = readw(IO_CONF_REG);
+ cntl_reg = readw(IO_CNTL_REG);
+ out_reg = readw(ARMIO_LATCH_OUT);
+ /* GPIO1 */
+ if (pinmux[0] & 0x80) {
+ cntl_reg &= ~(1 << 1);
+ if (pinmux[0] & 0x01)
+ out_reg |= (1 << 1);
+ else
+ out_reg &= ~(1 << 1);
+ }
+ /* GPIO2 */
+ if (pinmux[1] & 0x08) {
+ /* pinmux says it's DCD output - set it high */
+ cntl_reg &= ~(1 << 2);
+ out_reg |= (1 << 2);
+ } else if (pinmux[1] & 0x02) {
+ /* generic output */
+ cntl_reg &= ~(1 << 2);
+ if (pinmux[1] & 0x01)
+ out_reg |= (1 << 2);
+ else
+ out_reg &= ~(1 << 2);
+ }
+ /* GPIO3 */
+ if (pinmux[1] & 0x20) {
+ /* generic output */
+ cntl_reg &= ~(1 << 3);
+ if (pinmux[1] & 0x10)
+ out_reg |= (1 << 3);
+ else
+ out_reg &= ~(1 << 3);
+ }
+ /* MCSI or GPIO? */
+ if (pinmux[2] & 0x80) {
+ /* MCSI pins switch to GPIO */
+ conf_reg |= 0x1E0;
+ writew(conf_reg, IO_CONF_REG);
+ /* GPIO9 */
+ if (pinmux[3] & 0x10) {
+ cntl_reg &= ~(1 << 9);
+ if (pinmux[3] & 0x01)
+ out_reg |= (1 << 9);
+ else
+ out_reg &= ~(1 << 9);
+ } else
+ cntl_reg |= (1 << 9);
+ /* GPIO10 */
+ if (pinmux[3] & 0x20) {
+ cntl_reg &= ~(1 << 10);
+ if (pinmux[3] & 0x02)
+ out_reg |= (1 << 10);
+ else
+ out_reg &= ~(1 << 10);
+ }
+ /* GPIO11 */
+ if (pinmux[3] & 0x40) {
+ cntl_reg &= ~(1 << 11);
+ if (pinmux[3] & 0x04)
+ out_reg |= (1 << 11);
+ else
+ out_reg &= ~(1 << 11);
+ }
+ /* GPIO12 */
+ if (pinmux[3] & 0x80) {
+ cntl_reg &= ~(1 << 12);
+ if (pinmux[3] & 0x08)
+ out_reg |= (1 << 12);
+ else
+ out_reg &= ~(1 << 12);
+ }
+ }
+ writew(out_reg, ARMIO_LATCH_OUT);
+ writew(cntl_reg, IO_CNTL_REG);
+}
+
+void board_init(int with_irq)
+{
+ /*
+ * Configure the memory interface.
+ * nCS0 and nCS1 are internal flash and RAM - please refer to
+ * this technical article for an explanation of timing parameters:
+https://www.freecalypso.org/hg/freecalypso-docs/file/tip/MEMIF-wait-states
+ */
+ calypso_mem_cfg(CALYPSO_nCS0, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 4, CALYPSO_MEM_16bit, 1);
+ /* nCS2 and nCS3 are brought out for user-added custom hw */
+ calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1);
+ /* Calypso nCS4 is not brought out on TR-800, hence a dummy */
+ calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0);
+
+ /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */
+ calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2);
+
+ /* Configure the RHEA bridge with some sane default values */
+ calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0);
+
+ /* Initialize board-specific GPIO */
+ board_io_init();
+
+ /* Enable bootrom mapping to route exception vectors to RAM */
+ calypso_bootrom(with_irq);
+ calypso_exceptions_install();
+
+ /* Initialize interrupt controller */
+ if (with_irq)
+ irq_init();
+
+ /*
+ * The choice of which UART should be used for what is arbitrary -
+ * change to taste!
+ */
+ sercomm_bind_uart(UART_MODEM);
+ cons_bind_uart(UART_IRDA);
+
+ /* initialize MODEM UART to be used for sercomm */
+ uart_init(UART_MODEM, with_irq);
+ uart_baudrate(UART_MODEM, UART_115200);
+
+ /* Initialize IRDA UART to be used for old-school console code. */
+ uart_init(UART_IRDA, with_irq);
+ uart_baudrate(UART_IRDA, UART_115200);
+
+ /* Initialize hardware timers */
+ hwtimer_init();
+
+ /* Initialize DMA controller */
+ dma_init();
+
+ /* Initialize real time clock */
+ rtc_init();
+
+ /* Initialize system timers (uses hwtimer 2) */
+ timer_init();
+
+ /* Initialize keypad driver */
+ keypad_init(keymap, with_irq);
+
+ /* Initialize ABB driver (uses SPI) */
+ twl3025_init();
+
+ /* Initialize TIFFS reader (15 sectors of 64 KiB each) */
+ tiffs_init(0x700000, 0x10000, 15);
+
+ /* Initialize configurable pin multiplexing */
+ board_pinmux_init();
+}
diff --git a/src/target/firmware/board/tr800/keymap.h b/src/target/firmware/board/tr800/keymap.h
new file mode 100644
index 00000000..8e77ce2f
--- /dev/null
+++ b/src/target/firmware/board/tr800/keymap.h
@@ -0,0 +1,87 @@
+/*
+ * This code was written by Mychaela Falconia <falcon@freecalypso.org>
+ * who refuses to claim copyright on it and has released it as public domain
+ * instead. NO rights reserved, all rights relinquished.
+ *
+ * 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.
+ */
+
+/*
+ * The TR-800 module itself does not prescribe any particular keypad
+ * layout - instead all 5 KBC lines and all 5 KBR lines are simply
+ * brought out, allowing user applications to implement any desired
+ * keypad up to 5x5 buttons. When designing keypads for development
+ * boards (whether TR800-based or "raw" Calypso), Mother Mychaela's
+ * preference is to follow TI's original D-Sample key layout:
+
+ Main keypad (21 buttons):
+
+ L. Soft R. Soft
+ 5-way nav
+ Green Red
+ 1 2 3
+ 4 5 6
+ 7 8 9
+ * 0 #
+
+ Left side buttons: VOL+ / VOL-
+ Right side button: generic
+
+ Row/column matrix connections:
+
+ KBC0 KBC1 KBC2 KBC3 KBC4
+ KBR0 Green VOL- VOL+ L_Soft Nav_left
+ KBR1 1 2 3 R_Side Nav_right
+ KBR2 4 5 6 R_Soft Nav_up
+ KBR3 7 8 9 unused Nav_down
+ KBR4 * 0 # unused Nav_center
+
+ The red button is out-of-matrix PWON.
+
+ * If anyone has an original iWOW DSK board, the connection of
+ * "CALL" and "1" buttons on that board also matches the present
+ * D-Sample keymap.
+ */
+
+static const uint8_t keymap[] = {
+ [KEY_0] = 9,
+ [KEY_1] = 1,
+ [KEY_2] = 6,
+ [KEY_3] = 11,
+ [KEY_4] = 2,
+ [KEY_5] = 7,
+ [KEY_6] = 12,
+ [KEY_7] = 3,
+ [KEY_8] = 8,
+ [KEY_9] = 13,
+ [KEY_STAR] = 4,
+ [KEY_HASH] = 14,
+ [KEY_MENU] = 24,
+ [KEY_LEFT_SB] = 15,
+ [KEY_RIGHT_SB] = 17,
+ [KEY_UP] = 22,
+ [KEY_DOWN] = 23,
+ [KEY_LEFT] = 20,
+ [KEY_RIGHT] = 21,
+ [KEY_OK] = 0,
+/* power button is not connected to keypad scan matrix but to TWL3025 */
+ [KEY_POWER] = 31,
+/* D-Sample left side buttons for volume up/down control */
+ [KEY_MINUS] = 5,
+ [KEY_PLUS] = 10,
+/*
+ * D-Sample right side button can be seen as equivalent to
+ * Pirelli DP-L10 camera button, except for reversed history:
+ * D-Sample existed first and was used by the designers of the
+ * Pirelli DP-L10 phone as their starting point.
+ */
+ [KEY_CAMERA] = 16,
+};
diff --git a/src/target/firmware/board/tr800/rffe_leo_quadband.c b/src/target/firmware/board/tr800/rffe_leo_quadband.c
new file mode 100644
index 00000000..b1989a60
--- /dev/null
+++ b/src/target/firmware/board/tr800/rffe_leo_quadband.c
@@ -0,0 +1,194 @@
+/*
+ * This code was written by Mychaela Falconia <falcon@freecalypso.org>
+ * who refuses to claim copyright on it and has released it as public domain
+ * instead. NO rights reserved, all rights relinquished.
+ *
+ * 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.
+ */
+
+/*
+ * This module implements RFFE control for TI's original Leonardo+
+ * quadband RFFE, depicted on page 4 of this 2011-find schematic drawing:
+ *
+ * https://www.freecalypso.org/pub/GSM/Calypso/Leonardo_plus_quadband_schem.pdf
+ *
+ * This TI-original quadband RFFE is reproduced verbatim on the TR-800
+ * packaged module by iWOW.
+ *
+ * The present C code is based on ../gta0x/rffe_gta0x_triband.c,
+ * controlling Openmoko's triband RFFE which is very closely based on
+ * Leonardo, with only a few control signal permutations.
+ *
+ * The present code addition by Mother Mychaela merely brings the TR-800 hw
+ * target to the same level of support that already existed in OBB since
+ * forever for Compal/Motorola and Openmoko GTA01/02 targets, and more
+ * recently GTM900 and FCDEV3B - it does NOT fix the problem of overly
+ * simplistic RFFE control timing and other oversimplifications which OBB
+ * exhibits in comparison to the official firmware maintained by the
+ * custodians of the Calypso+Iota+RF chipset (formerly TI, now FreeCalypso).
+ * These massive oversimplifications which OBB exhibits in comparison to
+ * officially approved production firmwares result in OBB's radio transmissions
+ * being SEVERELY out of compliance (as observed with even the simplest tests
+ * with a CMU200 RF test instrument), thus anyone who runs the present code
+ * with Tx enabled outside of a Faraday cage will very likely cause
+ * interference and disruption to public communication networks! Furthermore,
+ * if you go on with running OBB with Tx enabled after having read this
+ * warning, the resulting interference and disruption to public communication
+ * networks can be considered intentional on your part, which is likely to be
+ * seen as a more severe offense.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rffe.h>
+#include <calypso/tsp.h>
+#include <rf/trf6151.h>
+
+/*
+ * OsmocomBB's definition of system inherent gain is similar to what is
+ * called "magic gain" (GMagic) in TI's architecture, except that TI's
+ * GMagic includes TRF6151 LNA gain whereas OBB's definition of system
+ * inherent gain does not. TI's GMagic is also reckoned in half-dB units
+ * instead of integral dB.
+ *
+ * The canonical GMagic number for Leonardo/TR-800 RFFE is 200, both in
+ * iWOW's original calibration and as confirmed with CMU200 measurements
+ * at FreeCalypso HQ. GMagic=200 in TI's universe is equivalent to
+ * OsmocomBB's "system inherent gain" of 73 dB.
+ */
+#define SYSTEM_INHERENT_GAIN 73
+
+/* describe how the RF frontend is wired on Leonardo and TR-800 */
+
+#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */
+#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */
+#define PA_BAND_SEL TSPACT(3) /* PA band select, 1=DCS/PCS */
+
+/* All FEM controls are low-active */
+#define FEM_7 TSPACT(2) /* FEM pin 7 */
+#define FEM_8 TSPACT(1) /* FEM pin 8 */
+#define FEM_9 TSPACT(4) /* FEM pin 9 */
+
+#define IOTA_STROBE TSPEN(0) /* Strobe for the Iota TSP */
+#define RITA_STROBE TSPEN(2) /* Strobe for the Rita TSP */
+
+/* switch RF Frontend Mode */
+void rffe_mode(enum gsm_band band, int tx)
+{
+ uint16_t tspact = tsp_act_state();
+
+ /* First we mask off all bits from the state cache */
+ tspact &= ~PA_ENABLE;
+ tspact &= ~PA_BAND_SEL;
+ tspact |= FEM_7 | FEM_8 | FEM_9; /* low-active */
+
+ switch (band) {
+ case GSM_BAND_850:
+ tspact &= ~FEM_9;
+ break;
+ case GSM_BAND_900:
+ case GSM_BAND_1800:
+ case GSM_BAND_1900:
+ break;
+ default:
+ /* TODO return/signal error here */
+ break;
+ }
+
+#ifdef CONFIG_TX_ENABLE
+ /* Then we selectively set the bits on, if required */
+ if (tx) {
+ switch (band) {
+ case GSM_BAND_850:
+ case GSM_BAND_900:
+ tspact |= FEM_9;
+ tspact &= ~FEM_7;
+ break;
+ case GSM_BAND_1800:
+ case GSM_BAND_1900:
+ tspact &= ~FEM_8;
+ tspact |= PA_BAND_SEL;
+ break;
+ default:
+ break;
+ }
+ tspact |= PA_ENABLE;
+ }
+#endif /* TRANSMIT_SUPPORT */
+
+ tsp_act_update(tspact);
+}
+
+/* Returns RF wiring */
+uint32_t rffe_get_rx_ports(void)
+{
+ return (1 << PORT_LO) | (1 << PORT_DCS1800) | (1 << PORT_PCS1900);
+}
+
+uint32_t rffe_get_tx_ports(void)
+{
+ return (1 << PORT_LO) | (1 << PORT_HI);
+}
+
+/* Returns need for IQ swap */
+int rffe_iq_swapped(uint16_t band_arfcn, int tx)
+{
+ return trf6151_iq_swapped(band_arfcn, tx);
+}
+
+
+#define MCU_SW_TRACE 0xfffef00e
+#define ARM_CONF_REG 0xfffef006
+
+void rffe_init(void)
+{
+ uint16_t reg;
+
+ reg = readw(ARM_CONF_REG);
+ reg &= ~(1 << 7); /* TSPACT4 I/O function, not nRDYMEM */
+ writew(reg, ARM_CONF_REG);
+
+ reg = readw(MCU_SW_TRACE);
+ reg &= ~(1 << 1); /* TSPACT9 I/O function, not MAS(1) */
+ writew(reg, MCU_SW_TRACE);
+
+ /* Configure the TSPEN which is connected to the TWL3025 */
+ tsp_setup(IOTA_STROBE, 1, 0, 0);
+
+ trf6151_init(RITA_STROBE, RITA_RESET);
+}
+
+uint8_t rffe_get_gain(void)
+{
+ return trf6151_get_gain();
+}
+
+void rffe_set_gain(uint8_t dbm)
+{
+ trf6151_set_gain(dbm);
+}
+
+const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN;
+
+/* Given the expected input level of exp_inp dBm/8 and the target of target_bb
+ * dBm8, configure the RF Frontend with the respective gain */
+void rffe_compute_gain(int16_t exp_inp, int16_t target_bb)
+{
+ trf6151_compute_gain(exp_inp, target_bb);
+}
+
+void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb)
+{
+ /* FIXME */
+}
diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c
index cf29984a..ba41bacb 100644
--- a/src/target/firmware/calypso/backlight.c
+++ b/src/target/firmware/calypso/backlight.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/buzzer.c b/src/target/firmware/calypso/buzzer.c
index e76906f9..4d9e60ba 100644
--- a/src/target/firmware/calypso/buzzer.c
+++ b/src/target/firmware/calypso/buzzer.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c
index 246b6e00..36a4ef11 100644
--- a/src/target/firmware/calypso/clock.c
+++ b/src/target/firmware/calypso/clock.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c
index 35c5be82..5580cb5f 100644
--- a/src/target/firmware/calypso/dma.c
+++ b/src/target/firmware/calypso/dma.c
@@ -14,10 +14,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 <memory.h>
diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c
index 235d359b..430c991a 100644
--- a/src/target/firmware/calypso/dsp.c
+++ b/src/target/firmware/calypso/dsp.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 <stdint.h>
diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c
index 58783b06..776cd74c 100644
--- a/src/target/firmware/calypso/du.c
+++ b/src/target/firmware/calypso/du.c
@@ -14,10 +14,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 <memory.h>
diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c
index bf441780..ad7d584c 100644
--- a/src/target/firmware/calypso/i2c.c
+++ b/src/target/firmware/calypso/i2c.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c
index 136fd55e..335bdc41 100644
--- a/src/target/firmware/calypso/irq.c
+++ b/src/target/firmware/calypso/irq.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c
index c3c1810b..e4d11fb1 100644
--- a/src/target/firmware/calypso/keypad.c
+++ b/src/target/firmware/calypso/keypad.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 <stdint.h>
diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c
index 45d759f3..288a1835 100644
--- a/src/target/firmware/calypso/rtc.c
+++ b/src/target/firmware/calypso/rtc.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/sim.c b/src/target/firmware/calypso/sim.c
index dc5885c4..ddc96946 100644
--- a/src/target/firmware/calypso/sim.c
+++ b/src/target/firmware/calypso/sim.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.
- *
*/
/* Uncomment to debug sim */
diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c
index 049ac080..a9b937ec 100644
--- a/src/target/firmware/calypso/spi.c
+++ b/src/target/firmware/calypso/spi.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c
index 1dd55f26..5141a837 100644
--- a/src/target/firmware/calypso/timer.c
+++ b/src/target/firmware/calypso/timer.c
@@ -14,10 +14,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>
diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c
index cbeef843..d84136c9 100644
--- a/src/target/firmware/calypso/tpu.c
+++ b/src/target/firmware/calypso/tpu.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c
index 5d24f48e..22ec538f 100644
--- a/src/target/firmware/calypso/tsp.c
+++ b/src/target/firmware/calypso/tsp.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c
index ec587de5..8dd96dad 100644
--- a/src/target/firmware/calypso/uart.c
+++ b/src/target/firmware/calypso/uart.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 <debug.h>
diff --git a/src/target/firmware/calypso/uwire.c b/src/target/firmware/calypso/uwire.c
index ac8f15e4..214433c8 100644
--- a/src/target/firmware/calypso/uwire.c
+++ b/src/target/firmware/calypso/uwire.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c
index 08f5acbd..c564e790 100644
--- a/src/target/firmware/comm/msgb.c
+++ b/src/target/firmware/comm/msgb.c
@@ -11,10 +11,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.
- *
*/
diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c
index cebd37dc..360dafea 100644
--- a/src/target/firmware/comm/sercomm.c
+++ b/src/target/firmware/comm/sercomm.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c
index e6b6934f..275ca5a4 100644
--- a/src/target/firmware/comm/sercomm_cons.c
+++ b/src/target/firmware/comm/sercomm_cons.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/comm/timer.c b/src/target/firmware/comm/timer.c
index ce4f06d3..44ef5519 100644
--- a/src/target/firmware/comm/timer.c
+++ b/src/target/firmware/comm/timer.c
@@ -12,10 +12,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 <stdint.h>
diff --git a/src/target/firmware/fb/fb_bw8.c b/src/target/firmware/fb/fb_bw8.c
index 0fc12ee5..95ea2dfc 100644
--- a/src/target/firmware/fb/fb_bw8.c
+++ b/src/target/firmware/fb/fb_bw8.c
@@ -16,10 +16,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 <stdlib.h>
diff --git a/src/target/firmware/fb/fb_dummy.c b/src/target/firmware/fb/fb_dummy.c
index cb053de4..b34656f5 100644
--- a/src/target/firmware/fb/fb_dummy.c
+++ b/src/target/firmware/fb/fb_dummy.c
@@ -17,10 +17,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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/fb_k2x0.c b/src/target/firmware/fb/fb_k2x0.c
new file mode 100644
index 00000000..7e433cd8
--- /dev/null
+++ b/src/target/firmware/fb/fb_k2x0.c
@@ -0,0 +1,190 @@
+/* Framebuffer implementation for SE K200i/K220i -
+ * combined driver for Core Logic CL761ST and S6B33B1X derivative */
+
+/* (C) 2022 by Steve Markgraf <steve@steve-m.de>
+ *
+ * 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
+ * (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.
+ *
+ */
+
+#include <fb/framebuffer.h>
+#include <fb/fb_rgb332.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <delay.h>
+#include <memory.h>
+
+#define K2X0_WIDTH 128
+#define K2X0_HEIGHT 128
+
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define CS3_ADDR 0x02000000
+
+#define DISPLAY_CMD_ADDR (CS3_ADDR + 0)
+#define DISPLAY_DATA_ADDR (CS3_ADDR + 2)
+#define CL761_INDEX_ADDR (CS3_ADDR + 4)
+#define CL761_DATA_ADDR (CS3_ADDR + 6)
+
+#define CL761_CLK (1 << 4)
+#define CL761_RESET (1 << 9)
+#define K2X0_ENABLE_BACKLIGHT (1 << 3)
+
+static uint8_t fb_k2x0_mem[K2X0_WIDTH * K2X0_HEIGHT];
+
+static const uint8_t k2x0_initdata[] = {
+ 0x2c, /* CMD: Standby Mode off */
+ 0x02, /* CMD: Oscillation Mode Set */
+ 0x01, /* DATA: oscillator on */
+ 0x26, /* CMD: DCDC and AMP ON/OFF set */
+ 0x01, /* DATA: Booster 1 on */
+ 0x26, /* CMD: DCDC and AMP ON/OFF set */
+ 0x09, /* DATA: Booster 1 on, OP-AMP on */
+ 0x26, /* CMD: DCDC and AMP ON/OFF set */
+ 0x0b, /* DATA: Booster 1 + 2 on, OP-AMP on */
+ 0x26, /* CMD: DCDC and AMP ON/OFF set */
+ 0x0f, /* DATA: Booster 1 + 2 + 3 on, OP-AMP on */
+ 0x10, /* CMD: Driver output mode set */
+ 0x00, /* DATA: Display duty: 1/66 */
+ 0x20, /* CMD: DC-DC Select */
+ 0x01, /* DATA: step up x1.5 */
+ 0x24, /* CMD: DCDC Clock Division Set */
+ 0x08, /* DATA: fPCK = fOSC/32 */
+ 0x28, /* CMD: Temperature Compensation set */
+ 0x02, /* DATA: slope -0.10%/degC */
+ 0x2a, /* CMD: Contrast Control */
+ 0x1d, /* DATA: Constrast Level 29 */
+ 0x30, /* CMD: Addressing mode set */
+ 0x53, /* DATA: 256 color mode (orignal FW uses 0x13, 65k colors) */
+ 0x32, /* CMD: ROW vector mode set */
+ 0x0e, /* DATA: every subframe */
+ 0x34, /* CMD: N-block inversion set */
+ 0x8d, /* DATA: inversion on, every 1 block and every 2 frames */
+ 0x36, /* CMD: unknown */
+ 0x00, /* DATA: unknown */
+ 0x40, /* CMD: Entry mode set */
+ 0x80, /* DATA: Y address counter mode */
+ 0x45, /* CMD: RAM Skip Area Set */
+ 0x00, /* DATA: No Skip */
+ 0x53, /* CMD: Specified Display Pattern Set */
+ 0x00, /* DATA: Normal display */
+ 0x55, /* CMD: Partial Display Mode Set */
+ 0x00, /* DATA: Partial display OFF */
+ 0x51, /* CMD: Display on */
+};
+
+uint16_t cl761_read_reg(uint16_t reg)
+{
+ writew(reg, CL761_INDEX_ADDR);
+ return readw(CL761_INDEX_ADDR);
+}
+
+void cl761_write_reg(uint8_t reg, uint16_t data)
+{
+ writew(reg, CL761_INDEX_ADDR);
+ writew(data, CL761_DATA_ADDR);
+}
+
+static void fb_k2x0_init(void)
+{
+ unsigned int i;
+ uint16_t reg;
+
+ printf("%s: initializing LCD.\n", __FUNCTION__);
+
+ reg = readw(ARMIO_LATCH_OUT);
+ reg &= ~(CL761_RESET | (1 << 1));
+ reg |= CL761_CLK;
+ writew(reg, ARMIO_LATCH_OUT);
+ delay_ms(10);
+ reg |= CL761_RESET;
+ writew(reg, ARMIO_LATCH_OUT);
+
+ /* we need to perform a dummy register read for the
+ * CL761 to pass through the chip select to the display */
+ cl761_read_reg(0x2e);
+ delay_ms(1);
+
+ reg &= ~CL761_CLK;
+ reg |= (1 << 1) | K2X0_ENABLE_BACKLIGHT;
+ writew(reg, ARMIO_LATCH_OUT);
+
+ for (i = 0; i < sizeof(k2x0_initdata); i++)
+ writew(k2x0_initdata[i], DISPLAY_CMD_ADDR);
+}
+
+static void fb_k2x0_flush(void)
+{
+ unsigned int i;
+ int x, y;
+ uint8_t *p;
+ uint8_t prepare_disp_write_cmds[] = {
+ 0x43, /* set column address */
+ fb_rgb332->damage_x1,
+ fb_rgb332->damage_x2 - 1,
+ 0x42, /* set page address (Y) */
+ fb_rgb332->damage_y1,
+ fb_rgb332->damage_y2 - 1,
+ };
+
+ /* If everything's clean, just return */
+ if (fb_rgb332->damage_x1 == fb_rgb332->damage_x2 ||
+ fb_rgb332->damage_y1 == fb_rgb332->damage_y2) {
+ printf("%s: no damage\n", __FUNCTION__);
+ return;
+ }
+
+ for (i = 0; i < sizeof(prepare_disp_write_cmds); i++)
+ writew(prepare_disp_write_cmds[i], DISPLAY_CMD_ADDR);
+
+ for (y = fb_rgb332->damage_y1; y < fb_rgb332->damage_y2; y++) {
+ p = & fb_rgb332->mem[y * framebuffer->width]; // start of line
+ p += fb_rgb332->damage_x1; // start of damage area
+
+ for (x = fb_rgb332->damage_x1; x < fb_rgb332->damage_x2; x++) {
+ /* For whatever reason, the 256 color mode of this
+ * display uses 'RBG323' */
+ uint8_t d = *p++;
+ d = (d & 0xe0) | ((d & 0x1c) >> 2) | ((d & 0x03) << 3);
+
+ /* We need to transfer the data twice in the 256 color mode.
+ * Interestingly, the red and green information is taken
+ * from the first byte written, and the blue information
+ * from the second byte written. */
+ writew(d, DISPLAY_DATA_ADDR);
+ writew(d, DISPLAY_DATA_ADDR);
+ }
+ }
+
+ fb_rgb332->damage_x1 = fb_rgb332->damage_x2 = 0;
+ fb_rgb332->damage_y1 = fb_rgb332->damage_y2 = 0;
+}
+
+static struct framebuffer fb_k2x0_framebuffer = {
+ .name = "k2x0",
+ .init = fb_k2x0_init,
+ .clear = fb_rgb332_clear,
+ .boxto = fb_rgb332_boxto,
+ .lineto = fb_rgb332_lineto,
+ .putstr = fb_rgb332_putstr,
+ .flush = fb_k2x0_flush,
+ .width = K2X0_WIDTH,
+ .height = K2X0_HEIGHT
+};
+
+static struct fb_rgb332 fb_k2x0_rgb332 = {
+ .mem = fb_k2x0_mem
+};
+
+struct framebuffer *framebuffer = &fb_k2x0_framebuffer;
+struct fb_rgb332 *fb_rgb332 = &fb_k2x0_rgb332;
diff --git a/src/target/firmware/fb/fb_rgb332.c b/src/target/firmware/fb/fb_rgb332.c
index 569ccc7d..73474888 100644
--- a/src/target/firmware/fb/fb_rgb332.c
+++ b/src/target/firmware/fb/fb_rgb332.c
@@ -17,10 +17,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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/fb_s6b33b1x.c b/src/target/firmware/fb/fb_s6b33b1x.c
index e36c5471..28fe5552 100644
--- a/src/target/firmware/fb/fb_s6b33b1x.c
+++ b/src/target/firmware/fb/fb_s6b33b1x.c
@@ -18,10 +18,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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/fb_ssd1783.c b/src/target/firmware/fb/fb_ssd1783.c
index cacdce03..3b39ad03 100644
--- a/src/target/firmware/fb/fb_ssd1783.c
+++ b/src/target/firmware/fb/fb_ssd1783.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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/fb_ssd1963.c b/src/target/firmware/fb/fb_ssd1963.c
index 361434e4..dee884dc 100644
--- a/src/target/firmware/fb/fb_ssd1963.c
+++ b/src/target/firmware/fb/fb_ssd1963.c
@@ -16,10 +16,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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/fb_st7558.c b/src/target/firmware/fb/fb_st7558.c
index f09b12b7..a2c6f502 100644
--- a/src/target/firmware/fb/fb_st7558.c
+++ b/src/target/firmware/fb/fb_st7558.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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/fb_td014.c b/src/target/firmware/fb/fb_td014.c
index c7bde0ca..b96fe94c 100644
--- a/src/target/firmware/fb/fb_td014.c
+++ b/src/target/firmware/fb/fb_td014.c
@@ -16,10 +16,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 <fb/framebuffer.h>
diff --git a/src/target/firmware/fb/font.c b/src/target/firmware/fb/font.c
index 18c1bfe9..7ca99121 100644
--- a/src/target/firmware/fb/font.c
+++ b/src/target/firmware/fb/font.c
@@ -14,10 +14,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 <fb/font.h>
diff --git a/src/target/firmware/fb/framebuffer.c b/src/target/firmware/fb/framebuffer.c
index ab547694..5132eeaf 100644
--- a/src/target/firmware/fb/framebuffer.c
+++ b/src/target/firmware/fb/framebuffer.c
@@ -14,10 +14,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 <fb/framebuffer.h>
diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c
index 2f8cde06..e34fa77f 100644
--- a/src/target/firmware/flash/cfi_flash.c
+++ b/src/target/firmware/flash/cfi_flash.c
@@ -14,10 +14,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 <debug.h>
diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h
index f2eae091..8f71d018 100644
--- a/src/target/firmware/include/calypso/du.h
+++ b/src/target/firmware/include/calypso/du.h
@@ -14,10 +14,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.
- *
*/
#ifndef _CALYPSO_DU_H
diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h
index d49866e2..476a8a86 100644
--- a/src/target/firmware/include/calypso/l1_environment.h
+++ b/src/target/firmware/include/calypso/l1_environment.h
@@ -104,6 +104,8 @@ typedef signed short API_SIGNED;
#define B_PLAY_UL (1 << 3) // Play UL
#define B_DCO_ON (1 << 4) // DCO ON/OFF
#define B_AUDIO_ASYNC (1 << 1) // WCP reserved
+#define B_MUTE_VOCODEC_DL (1 << 14) // DL voice decoder
+#define B_MUTE_VOCODEC_UL (1 << 15) // UL voice encoder
// ****************************************************************
// PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS
diff --git a/src/target/firmware/include/calypso/sim.h b/src/target/firmware/include/calypso/sim.h
index 8f627b18..686e5781 100755
--- a/src/target/firmware/include/calypso/sim.h
+++ b/src/target/firmware/include/calypso/sim.h
@@ -14,10 +14,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.
- *
*/
#ifndef _CALYPSO_SIM_H
diff --git a/src/target/firmware/include/comm/timer.h b/src/target/firmware/include/comm/timer.h
index d43b067e..e7c518dd 100644
--- a/src/target/firmware/include/comm/timer.h
+++ b/src/target/firmware/include/comm/timer.h
@@ -12,10 +12,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.
- *
*/
#ifndef TIMER_H
diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h
index 221ffcf9..b383d477 100644
--- a/src/target/firmware/include/layer1/async.h
+++ b/src/target/firmware/include/layer1/async.h
@@ -47,6 +47,9 @@ uint8_t l1a_tch_mode_set(uint8_t mode);
/* Set Audio routing mode */
uint8_t l1a_audio_mode_set(uint8_t mode);
+/* Set TCH flags */
+uint8_t l1a_tch_flags_set(uint8_t flags);
+
/* Execute pending L1A completions */
void l1a_compl_execute(void);
diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h
index 30c51ae6..4e34bb23 100644
--- a/src/target/firmware/include/layer1/prim.h
+++ b/src/target/firmware/include/layer1/prim.h
@@ -20,7 +20,7 @@ void l1s_nb_test(uint8_t base_fn);
void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req);
void l1a_freq_req(uint32_t fn_sched);
-void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra);
+void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint8_t uic);
/* Primitives raw scheduling sets */
extern const struct tdma_sched_item nb_sched_set[];
diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h
index dd932421..e0a4412f 100644
--- a/src/target/firmware/include/layer1/sync.h
+++ b/src/target/firmware/include/layer1/sync.h
@@ -82,6 +82,7 @@ struct l1s_state {
uint8_t tch_mode;
uint8_t tch_sync;
uint8_t audio_mode;
+ uint8_t tch_flags;
/* 3GPP TS 44.014, section 5.1 (Calypso DSP specific numbers) */
enum l1ctl_tch_loop_mode tch_loop_mode;
@@ -116,6 +117,7 @@ struct l1s_state {
struct {
uint8_t ra;
+ uint8_t uic;
} rach;
struct {
diff --git a/src/target/firmware/include/mtk/emi.h b/src/target/firmware/include/mtk/emi.h
index 18184992..ee1399c2 100644
--- a/src/target/firmware/include/mtk/emi.h
+++ b/src/target/firmware/include/mtk/emi.h
@@ -14,10 +14,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.
- *
*/
#ifndef __MTK_EMI_H_
diff --git a/src/target/firmware/include/mtk/mt6235.h b/src/target/firmware/include/mtk/mt6235.h
index fb9d368e..2d36bee6 100644
--- a/src/target/firmware/include/mtk/mt6235.h
+++ b/src/target/firmware/include/mtk/mt6235.h
@@ -14,10 +14,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.
- *
*/
#ifndef __MT6235_H
diff --git a/src/target/firmware/include/mtk/system.h b/src/target/firmware/include/mtk/system.h
index 45430291..9c28b56c 100644
--- a/src/target/firmware/include/mtk/system.h
+++ b/src/target/firmware/include/mtk/system.h
@@ -14,10 +14,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.
- *
*/
#ifndef __MTK_SYSTEM_H_
diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c
index 1a198e3d..f4533ad3 100644
--- a/src/target/firmware/layer1/afc.c
+++ b/src/target/firmware/layer1/afc.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c
index b72a6e74..66e7f092 100644
--- a/src/target/firmware/layer1/agc.c
+++ b/src/target/firmware/layer1/agc.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/async.c b/src/target/firmware/layer1/async.c
index cb2a2a8c..8d76e5cc 100644
--- a/src/target/firmware/layer1/async.c
+++ b/src/target/firmware/layer1/async.c
@@ -14,10 +14,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 <stdint.h>
@@ -113,6 +109,10 @@ uint8_t l1a_tch_mode_set(uint8_t mode)
switch (mode) {
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_DATA_14k5:
+ case GSM48_CMODE_DATA_12k0:
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
l1s.tch_mode = mode;
break;
default:
@@ -129,6 +129,13 @@ uint8_t l1a_audio_mode_set(uint8_t mode)
return mode;
}
+/* Set TCH flags */
+uint8_t l1a_tch_flags_set(uint8_t flags)
+{
+ l1s.tch_flags = flags;
+ return flags;
+}
+
/* Initialize asynchronous part of Layer1 */
void l1a_init(void)
{
diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c
index a4bf565b..7211c218 100644
--- a/src/target/firmware/layer1/avg.c
+++ b/src/target/firmware/layer1/avg.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c
index e7fde232..602e2286 100644
--- a/src/target/firmware/layer1/init.c
+++ b/src/target/firmware/layer1/init.c
@@ -14,10 +14,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.
- *
*/
diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c
index 4621bfca..f4309e2a 100644
--- a/src/target/firmware/layer1/l23_api.c
+++ b/src/target/firmware/layer1/l23_api.c
@@ -14,10 +14,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.
- *
*/
#define DEBUG
@@ -77,72 +73,75 @@ static uint32_t chan_nr2mf_task_mask(uint8_t chan_nr, uint8_t neigh_mode)
uint8_t cbits = chan_nr >> 3;
uint8_t tn = chan_nr & 0x7;
uint8_t lch_idx;
- enum mframe_task master_task = MF_TASK_BCCH_NORM;
- enum mframe_task second_task = -1; /* optional */
enum mf_type multiframe = 0;
uint32_t task_mask = 0x00;
+#define TASK_SET(task) \
+ task_mask |= (1 << (task))
+
if (cbits == 0x01) {
lch_idx = 0;
- master_task = (tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN;
+ TASK_SET((tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN);
multiframe = (tn & 1) ? MF26ODD : MF26EVEN;
} else if ((cbits & 0x1e) == 0x02) {
lch_idx = cbits & 0x1;
- master_task = MF_TASK_TCH_H_0 + lch_idx;
+ TASK_SET(MF_TASK_TCH_H_0 + lch_idx);
multiframe = (lch_idx & 1) ? MF26ODD : MF26EVEN;
} else if ((cbits & 0x1c) == 0x04) {
lch_idx = cbits & 0x3;
- master_task = MF_TASK_SDCCH4_0 + lch_idx;
+ TASK_SET(MF_TASK_SDCCH4_0 + lch_idx);
multiframe = MF51;
} else if ((cbits & 0x18) == 0x08) {
lch_idx = cbits & 0x7;
- master_task = MF_TASK_SDCCH8_0 + lch_idx;
+ TASK_SET(MF_TASK_SDCCH8_0 + lch_idx);
multiframe = MF51;
} else if ((cbits & 0x1f) == 0x18) {
/* Osmocom specific extension for PDTCH and PTCCH */
- master_task = MF_TASK_GPRS_PDTCH;
- second_task = MF_TASK_GPRS_PTCCH;
+ TASK_SET(MF_TASK_GPRS_PDTCH);
+ TASK_SET(MF_TASK_GPRS_PTCCH);
/* FIXME: PDCH has different multiframe structure */
multiframe = MFNONE;
} else if ((cbits & 0x1f) == 0x19) {
/* Osmocom specific extension for CBCH on SDCCH/4 */
- master_task = MF_TASK_SDCCH4_CBCH;
+ TASK_SET(MF_TASK_SDCCH4_CBCH);
multiframe = MF51;
} else if ((cbits & 0x1f) == 0x1a) {
/* Osmocom specific extension for CBCH on SDCCH/8 */
- master_task = MF_TASK_SDCCH8_CBCH;
+ TASK_SET(MF_TASK_SDCCH8_CBCH);
multiframe = MF51;
#if 0
} else if (cbits == 0x10) {
/* FIXME: when to do extended BCCH? */
- master_task = MF_TASK_BCCH_NORM;
+ TASK_SET(MF_TASK_BCCH_NORM);
} else if (cbits == 0x11 || cbits == 0x12) {
/* FIXME: how to decide CCCH norm/extd? */
- master_task = MF_TASK_BCCH_CCCH;
+ TASK_SET(MF_TASK_BCCH_CCCH);
#endif
+ } else {
+ TASK_SET(MF_TASK_BCCH_NORM);
}
- /* Primary and secondary tasks */
- task_mask |= (1 << master_task);
- if (second_task >= 0) /* optional */
- task_mask |= (1 << second_task);
-
switch (neigh_mode) {
case NEIGH_MODE_PM:
switch (multiframe) {
case MF51:
- task_mask |= (1 << MF_TASK_NEIGH_PM51);
+ TASK_SET(MF_TASK_NEIGH_PM51);
break;
case MF26EVEN:
- task_mask |= (1 << MF_TASK_NEIGH_PM26E);
+ TASK_SET(MF_TASK_NEIGH_PM26E);
break;
case MF26ODD:
- task_mask |= (1 << MF_TASK_NEIGH_PM26O);
+ TASK_SET(MF_TASK_NEIGH_PM26O);
+ break;
+ default:
+ /* no neighbor measurement */
break;
}
break;
}
+#undef TASK_SET
+
return task_mask;
}
@@ -258,8 +257,8 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg)
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload;
- printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n",
- ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc);
+ printd("L1CTL_DM_EST_REQ (chan_nr=0x%02x, tsc=%u)\n",
+ ul->chan_nr, est_req->tsc);
/* disable neighbour cell measurement of C0 TS 0 */
mframe_disable(MF_TASK_NEIGH_PM51_C0T0);
@@ -277,8 +276,12 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg)
l1s.dedicated.h1.n = est_req->h1.n;
for (i=0; i<est_req->h1.n; i++)
l1s.dedicated.h1.ma[i] = ntohs(est_req->h1.ma[i]);
+ printd("L1CTL_DM_EST_REQ indicates H1 (HSN=%u, MAIO=%u, chans=%u)\n",
+ est_req->h1.hsn, est_req->h1.maio, est_req->h1.n);
} else {
l1s.dedicated.h0.arfcn = ntohs(est_req->h0.band_arfcn);
+ printd("L1CTL_DM_EST_REQ indicates H0 (ARFCN=%u)\n",
+ l1s.dedicated.h0.arfcn & ~ARFCN_FLAG_MASK);
}
/* TCH config */
@@ -286,6 +289,7 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg)
/* Mode */
l1a_tch_mode_set(est_req->tch_mode);
l1a_audio_mode_set(est_req->audio_mode);
+ l1a_tch_flags_set(est_req->tch_flags);
/* Sync */
l1s.tch_sync = 1; /* can be set without locking */
@@ -347,8 +351,6 @@ static void l1ctl_rx_crypto_req(struct msgb *msg)
/* receive a L1CTL_DM_REL_REQ from L23 */
static void l1ctl_rx_dm_rel_req(struct msgb *msg)
{
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
-
printd("L1CTL_DM_REL_REQ\n");
l1a_mftask_set(0);
l1s.dedicated.type = GSM_DCHAN_NONE;
@@ -384,11 +386,10 @@ static void l1ctl_rx_rach_req(struct msgb *msg)
struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload;
- printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d, combined=%d)\n",
- rach_req->ra, ntohs(rach_req->offset), rach_req->combined);
+ printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d, combined=%d, uic=0x%02x)\n",
+ rach_req->ra, ntohs(rach_req->offset), rach_req->combined, rach_req->uic);
- l1a_rach_req(ntohs(rach_req->offset), rach_req->combined,
- rach_req->ra);
+ l1a_rach_req(ntohs(rach_req->offset), rach_req->combined, rach_req->ra, rach_req->uic);
}
/* receive a L1CTL_DATA_REQ from L23 */
@@ -521,7 +522,7 @@ static void l1ctl_rx_ccch_mode_req(struct msgb *msg)
}
/* Transmit a L1CTL_TCH_MODE_CONF */
-static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode)
+static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags)
{
struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TCH_MODE_CONF);
struct l1ctl_tch_mode_conf *mode_conf;
@@ -529,6 +530,7 @@ static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode)
msgb_put(msg, sizeof(*mode_conf));
mode_conf->tch_mode = tch_mode;
mode_conf->audio_mode = audio_mode;
+ mode_conf->tch_flags = l1s.tch_flags;
mode_conf->tch_loop_mode = l1s.tch_loop_mode;
l1_queue_for_l2(msg);
@@ -542,18 +544,22 @@ static void l1ctl_rx_tch_mode_req(struct msgb *msg)
(struct l1ctl_tch_mode_req *) l1h->data;
uint8_t tch_mode = tch_mode_req->tch_mode;
uint8_t audio_mode = tch_mode_req->audio_mode;
+ uint8_t tch_flags = tch_mode_req->tch_flags;
printd("L1CTL_TCH_MODE_REQ (tch_mode=0x%02x audio_mode=0x%02x)\n",
tch_mode, audio_mode);
+
tch_mode = l1a_tch_mode_set(tch_mode);
audio_mode = l1a_audio_mode_set(audio_mode);
+ tch_flags = l1a_tch_flags_set(tch_flags);
audio_set_enabled(tch_mode, audio_mode);
l1s.tch_sync = 1; /* Needed for audio to work */
l1s.tch_loop_mode = tch_mode_req->tch_loop_mode;
+ /* TODO: Handle AMR codecs from tch_mode_req if tch_mode_req->tch_mode==GSM48_CMODE_SPEECH_AMR */
- l1ctl_tx_tch_mode_conf(tch_mode, audio_mode);
+ l1ctl_tx_tch_mode_conf(tch_mode, audio_mode, tch_flags);
}
/* receive a L1CTL_NEIGH_PM_REQ from L23 */
@@ -725,4 +731,3 @@ void l1a_l23api_init(void)
{
sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx);
}
-
diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c
index fac84b43..478fd20a 100644
--- a/src/target/firmware/layer1/mframe_sched.c
+++ b/src/target/firmware/layer1/mframe_sched.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c
index 8eaeb5af..4f4c4fd5 100644
--- a/src/target/firmware/layer1/prim_fbsb.c
+++ b/src/target/firmware/layer1/prim_fbsb.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/prim_freq.c b/src/target/firmware/layer1/prim_freq.c
index d5b5df15..a6ddc545 100644
--- a/src/target/firmware/layer1/prim_freq.c
+++ b/src/target/firmware/layer1/prim_freq.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c
index 5c8c4914..11bee99b 100644
--- a/src/target/firmware/layer1/prim_pm.c
+++ b/src/target/firmware/layer1/prim_pm.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c
index e6ea6568..580131ec 100644
--- a/src/target/firmware/layer1/prim_rach.c
+++ b/src/target/firmware/layer1/prim_rach.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 <stdint.h>
@@ -66,7 +62,11 @@ static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused ui
l1s_tx_apc_helper(l1s.serving_cell.arfcn);
- data[0] = l1s.serving_cell.bsic << 2;
+ /* If an invalid UIC is given, use BSIC. */
+ if (l1s.rach.uic > 0x3f)
+ data[0] = l1s.serving_cell.bsic << 2;
+ else
+ data[0] = l1s.rach.uic << 2;
data[1] = l1s.rach.ra;
info_ptr = &dsp_api.ndb->d_rach;
@@ -131,8 +131,8 @@ static uint8_t rach_to_t3_comb[27] = {
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
45, 46};
-/* request a RACH request at the next multiframe T3 = fn51 */
-void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra)
+/* schedule access burst */
+void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint8_t uic)
{
uint32_t fn_sched;
unsigned long flags;
@@ -140,7 +140,19 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra)
offset += 3;
local_firq_save(flags);
- if (combined) {
+ if (l1s.dedicated.type == GSM_DCHAN_TCH_F) {
+ fn_sched = l1s.current_time.fn + offset;
+ /* go next DCCH frame TCH/F channel */
+ if ((fn_sched % 13) == 12)
+ fn_sched++;
+ } else if (l1s.dedicated.type == GSM_DCHAN_TCH_H) {
+ fn_sched = l1s.current_time.fn + offset;
+ /* go next DCCH frame of TCH/H channel */
+ if ((fn_sched % 13) == 12)
+ fn_sched++;
+ if ((l1s.dedicated.tn & 1) != ((fn_sched % 13) & 1))
+ fn_sched++;
+ } else if (combined) {
/* add elapsed RACH slots to offset */
offset += t3_to_rach_comb[l1s.current_time.t3];
/* offset is the number of RACH slots in the future */
@@ -150,6 +162,8 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra)
} else
fn_sched = l1s.current_time.fn + offset;
l1s.rach.ra = ra;
+ l1s.rach.uic = uic;
+ fn_sched %= GSM_MAX_FN;
sched_gsmtime(rach_sched_set_ul, fn_sched, 0);
local_irq_restore(flags);
diff --git a/src/target/firmware/layer1/prim_rx_nb.c b/src/target/firmware/layer1/prim_rx_nb.c
index c90c98cb..d6dd82a5 100644
--- a/src/target/firmware/layer1/prim_rx_nb.c
+++ b/src/target/firmware/layer1/prim_rx_nb.c
@@ -14,10 +14,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 <stdint.h>
@@ -126,7 +122,7 @@ static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3)
/* Set SACCH indication in Link IDentifier */
if (mf_task_flags & MF_F_SACCH)
rxnb.dl->link_id = 0x40;
- if (mf_task_flags & MF_F_PTCCH)
+ else if (mf_task_flags & MF_F_PTCCH)
rxnb.dl->link_id = 0x80;
else
rxnb.dl->link_id = 0x00;
diff --git a/src/target/firmware/layer1/prim_tch.c b/src/target/firmware/layer1/prim_tch.c
index 40ef8f3c..1bc842b2 100644
--- a/src/target/firmware/layer1/prim_tch.c
+++ b/src/target/firmware/layer1/prim_tch.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 <stdint.h>
@@ -32,7 +28,6 @@
#include <byteorder.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/codec/codec.h>
#include <osmocom/core/msgb.h>
#include <calypso/dsp_api.h>
#include <calypso/irq.h>
@@ -56,45 +51,6 @@
#include <l1ctl_proto.h>
-static inline int msb_get_bit(uint8_t *buf, int bn)
-{
- int pos_byte = bn >> 3;
- int pos_bit = 7 - (bn & 7);
-
- return (buf[pos_byte] >> pos_bit) & 1;
-}
-
-static inline void msb_set_bit(uint8_t *buf, int bn, int bit)
-{
- int pos_byte = bn >> 3;
- int pos_bit = 7 - (bn & 7);
-
- buf[pos_byte] |= (bit << pos_bit);
-}
-
-static void tch_fr_bit_magic(uint8_t *frame, int dl)
-{
- uint8_t fr[33];
- int i, di, si;
-
- memset(fr, 0x00, 33);
-
- if (dl)
- fr[0] = 0xd0;
-
- for (i = 0; i < 260; i++) {
- di = gsm610_bitorder[i];
- si = (i > 181) ? i + 4 : i;
-
- if (dl)
- msb_set_bit(fr, 4 + di, msb_get_bit(frame, si));
- else
- msb_set_bit(fr, si, msb_get_bit(frame, 4 + di));
- }
-
- memcpy(frame, fr, 33);
-}
-
/* This computes various parameters both for the DSP and for
* our logic. Not all are used all the time, but it's easier
* to build all in one place */
@@ -125,10 +81,34 @@ static void tch_get_params(struct gsm_time *time, uint8_t chan_nr,
case GSM48_CMODE_SPEECH_EFR:
*tch_mode = *tch_f_hn ? TCH_EFR_MODE : SIG_ONLY_MODE;
break;
+ case GSM48_CMODE_DATA_14k5:
+ *tch_mode = *tch_f_hn ? TCH_144_MODE : SIG_ONLY_MODE;
+ break;
+ case GSM48_CMODE_DATA_12k0:
+ *tch_mode = *tch_f_hn ? TCH_96_MODE : SIG_ONLY_MODE;
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ *tch_mode = *tch_f_hn ? TCH_48F_MODE : TCH_48H_MODE;
+ break;
+ case GSM48_CMODE_DATA_3k6:
+ *tch_mode = *tch_f_hn ? TCH_24F_MODE : TCH_24H_MODE;
+ break;
default:
*tch_mode = SIG_ONLY_MODE;
}
}
+
+ /* enable/disable the voice decoder (Downlink) */
+ if (l1s.audio_mode & AUDIO_RX_SPEAKER)
+ dsp_api.ndb->d_tch_mode &= ~B_MUTE_VOCODEC_DL; /* unmute */
+ else
+ dsp_api.ndb->d_tch_mode |= B_MUTE_VOCODEC_DL; /* mute */
+
+ /* enable/disable the voice encoder (Uplink) */
+ if (l1s.audio_mode & AUDIO_TX_MICROPHONE)
+ dsp_api.ndb->d_tch_mode &= ~B_MUTE_VOCODEC_UL; /* unmute */
+ else
+ dsp_api.ndb->d_tch_mode |= B_MUTE_VOCODEC_UL; /* mute */
}
@@ -188,14 +168,31 @@ static __attribute__ ((constructor)) void prim_tch_init(void)
#define FACCH_MEAS_HIST 8 /* Up to 8 bursts history */
struct l1s_rx_tch_state {
struct l1s_meas_hdr meas[FACCH_MEAS_HIST];
+ uint8_t meas_id;
};
static struct l1s_rx_tch_state rx_tch;
+static inline void l1s_tch_meas_avg(struct l1ctl_info_dl *dl,
+ unsigned int meas_num)
+{
+ uint32_t avg_snr = 0;
+ int32_t avg_dbm8 = 0;
+ unsigned int i;
+
+ for (i = 0; i < meas_num; i++) {
+ int j = (rx_tch.meas_id + FACCH_MEAS_HIST - i) % FACCH_MEAS_HIST;
+ avg_snr += rx_tch.meas[j].snr;
+ avg_dbm8 += rx_tch.meas[j].pm_dbm8;
+ }
+
+ dl->snr = avg_snr / meas_num;
+ dl->rx_level = dbm2rxlev(avg_dbm8 / (8 * meas_num));
+}
+
static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
{
- static uint8_t meas_id = 0;
uint8_t mf_task_id = p3 & 0xff;
struct gsm_time rx_time;
uint8_t chan_nr;
@@ -211,27 +208,27 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
chan_nr = mframe_task2chan_nr(mf_task_id, tn);
tch_get_params(&rx_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, NULL);
- meas_id = (meas_id + 1) % FACCH_MEAS_HIST; /* absolute value doesn't matter */
+ rx_tch.meas_id = (rx_tch.meas_id + 1) % FACCH_MEAS_HIST; /* absolute value doesn't matter */
/* Collect measurements */
- rx_tch.meas[meas_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA];
- rx_tch.meas[meas_id].pm_dbm8 =
+ rx_tch.meas[rx_tch.meas_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA];
+ rx_tch.meas[rx_tch.meas_id].pm_dbm8 =
agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3);
- rx_tch.meas[meas_id].freq_err =
+ rx_tch.meas[rx_tch.meas_id].freq_err =
ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]);
- rx_tch.meas[meas_id].snr = dsp_api.db_r->a_serv_demod[D_SNR];
+ rx_tch.meas[rx_tch.meas_id].snr = dsp_api.db_r->a_serv_demod[D_SNR];
/* feed computed frequency error into AFC loop */
- if (rx_tch.meas[meas_id].snr > AFC_SNR_THRESHOLD)
- afc_input(rx_tch.meas[meas_id].freq_err, arfcn, 1);
+ if (rx_tch.meas[rx_tch.meas_id].snr > AFC_SNR_THRESHOLD)
+ afc_input(rx_tch.meas[rx_tch.meas_id].freq_err, arfcn, 1);
else
- afc_input(rx_tch.meas[meas_id].freq_err, arfcn, 0);
+ afc_input(rx_tch.meas[rx_tch.meas_id].freq_err, arfcn, 0);
/* feed computed TOA into TA loop */
- toa_input(rx_tch.meas[meas_id].toa_qbit << 2, rx_tch.meas[meas_id].snr);
+ toa_input(rx_tch.meas[rx_tch.meas_id].toa_qbit << 2, rx_tch.meas[rx_tch.meas_id].snr);
/* Tell the RF frontend to set the gain appropriately */
- rffe_compute_gain(rx_tch.meas[meas_id].pm_dbm8 / 8,
+ rffe_compute_gain(rx_tch.meas[rx_tch.meas_id].pm_dbm8 / 8,
CAL_DSP_TGT_BB_LVL);
/* FACCH Block end ? */
@@ -251,9 +248,6 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
struct l1ctl_info_dl *dl;
struct l1ctl_data_ind *di;
uint16_t num_biterr;
- uint32_t avg_snr = 0;
- int32_t avg_dbm8 = 0;
- int i, n;
/* Allocate msgb */
/* FIXME: we actually want all allocation out of L1S! */
@@ -273,15 +267,7 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
dl->frame_nr = htonl(rx_time.fn);
/* Average SNR & RX level */
- n = tch_f_hn ? 8 : 6;
- for (i=0; i<n; i++) {
- int j = (meas_id + FACCH_MEAS_HIST - i) % FACCH_MEAS_HIST;
- avg_snr += rx_tch.meas[j].snr;
- avg_dbm8 += rx_tch.meas[j].pm_dbm8;
- }
-
- dl->snr = avg_snr / n;
- dl->rx_level = dbm2rxlev(avg_dbm8 / (8*n));
+ l1s_tch_meas_avg(dl, tch_f_hn ? 8 : 6);
/* Errors & CRC status */
num_biterr = dsp_api.ndb->a_fd[2] & 0xffff;
@@ -301,7 +287,7 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
/* Give message to up layer */
l1_queue_for_l2(msg);
- skip_rx_facch:
+skip_rx_facch:
/* Reset A_FD header (needed by DSP) */
/* B_FIRE1 =1, B_FIRE0 =0 , BLUD =0 */
dsp_api.ndb->a_fd[0] = (1<<B_FIRE1);
@@ -328,48 +314,63 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
if (traffic_rx_now) {
volatile uint16_t *traffic_buf;
+ struct l1ctl_info_dl *dl;
+ struct l1ctl_traffic_ind *ti;
+ struct msgb *msg;
+ uint16_t num_biterr;
traffic_buf = tch_sub ? dsp_api.ndb->a_dd_1 : dsp_api.ndb->a_dd_0;
- if (traffic_buf[0] & (1<<B_BLUD)) {
- /* Send the data to upper layers (if interested and good frame) */
- if ((l1s.audio_mode & AUDIO_RX_TRAFFIC_IND) &&
- !(dsp_api.ndb->a_dd_0[0] & (1<<B_BFI))) {
- struct msgb *msg;
- struct l1ctl_info_dl *dl;
- struct l1ctl_traffic_ind *ti;
- uint8_t *payload;
-
- /* Allocate msgb */
- /* FIXME: we actually want all allocation out of L1S! */
- msg = l1ctl_msgb_alloc(L1CTL_TRAFFIC_IND);
- if(!msg) {
- printf("TCH traffic: unable to allocate msgb\n");
- goto skip_rx_traffic;
- }
-
- dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl));
- ti = (struct l1ctl_traffic_ind *) msgb_put(msg, sizeof(*ti));
- payload = (uint8_t *) msgb_put(msg, 33);
-
- /* Copy actual data, skipping the information block [0,1,2] */
- dsp_memcpy_from_api(payload, &traffic_buf[3], 33, 1);
-
- /**
- * Perform some bit conversations
- * FIXME: what about other (than FR) codecs?
- */
- tch_fr_bit_magic(payload, 1);
-
- /* Give message to up layer */
- l1_queue_for_l2(msg);
- }
-
- skip_rx_traffic:
- /* Reset traffic buffer header in NDB (needed by DSP) */
- traffic_buf[0] = 0;
- traffic_buf[2] = 0xffff;
+ /* Send the data to upper layers (if interested and good frame) */
+ if (~l1s.audio_mode & AUDIO_RX_TRAFFIC_IND)
+ goto skip_rx_traffic;
+ if (~traffic_buf[0] & (1 << B_BLUD))
+ goto skip_rx_traffic;
+
+ /* Allocate msgb */
+ /* FIXME: we actually want all allocation out of L1S! */
+ msg = l1ctl_msgb_alloc(L1CTL_TRAFFIC_IND);
+ if (!msg) {
+ printf("TCH traffic: unable to allocate msgb\n");
+ goto skip_rx_traffic;
}
+
+ dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl));
+ ti = (struct l1ctl_traffic_ind *) msgb_put(msg, sizeof(*ti));
+
+ dl->chan_nr = chan_nr;
+ dl->band_arfcn = htons(arfcn);
+ dl->frame_nr = htonl(rx_time.fn);
+
+ /* Average SNR & RX level */
+ l1s_tch_meas_avg(dl, tch_f_hn ? 8 : 4);
+
+ /* Errors & CRC status */
+ num_biterr = traffic_buf[2] & 0xffff;
+ if (num_biterr > 0xff)
+ dl->num_biterr = 0xff;
+ else
+ dl->num_biterr = num_biterr;
+
+ dl->fire_crc = ((traffic_buf[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0;
+
+ /* Update rx level for pm report */
+ pu_update_rx_level(dl->rx_level);
+
+ /* Copy actual data, skipping the information block [0,1,2] */
+ dsp_memcpy_from_api(msgb_put(msg, 33), &traffic_buf[3], 33, 1);
+
+ /* Give message to up layer */
+ l1_queue_for_l2(msg);
+
+skip_rx_traffic:
+ /* Reset A_DD_0 header in NDB (needed by DSP) */
+ dsp_api.ndb->a_dd_0[0] = 0;
+ dsp_api.ndb->a_dd_0[2] = 0xffff;
+
+ /* Reset A_DD_1 header in NDB (needed by DSP) */
+ dsp_api.ndb->a_dd_1[0] = 0;
+ dsp_api.ndb->a_dd_1[2] = 0xffff;
}
/* mark READ page as being used */
@@ -472,7 +473,6 @@ static int l1s_tch_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
if (traffic_tx_now) {
volatile uint16_t *traffic_buf;
struct msgb *msg;
- const uint8_t *data;
/* Reset play mode */
dsp_api.ndb->d_tch_mode &= ~B_PLAY_UL;
@@ -486,37 +486,25 @@ static int l1s_tch_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
/* Pull Traffic data (if any) */
msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_TRAFFIC]);
-
- /**
- * Perform some bit conversations
- * FIXME: what about other (than FR) codecs?
- */
- if (msg)
- tch_fr_bit_magic(msg->l2h, 0);
+ if (msg == NULL)
+ goto skip_tx_traffic;
/* Copy actual data, skipping the information block [0,1,2] */
- if (msg) {
- data = msg->l2h;
- dsp_memcpy_to_api(&traffic_buf[3], data, 33, 1);
+ dsp_memcpy_to_api(&traffic_buf[3], msgb_l2(msg), 33, 1);
- traffic_buf[0] = (1 << B_BLUD); /* 1st word: Set B_BLU bit. */
- traffic_buf[1] = 0; /* 2nd word: cleared. */
- traffic_buf[2] = 0; /* 3nd word: cleared. */
- }
+ traffic_buf[0] = (1 << B_BLUD); /* 1st word: Set B_BLU bit. */
+ traffic_buf[1] = 0; /* 2nd word: cleared. */
+ traffic_buf[2] = 0; /* 3nd word: cleared. */
- if (msg)
- dsp_api.ndb->d_tch_mode |= B_PLAY_UL;
+ dsp_api.ndb->d_tch_mode |= B_PLAY_UL;
/* Indicate completion (FIXME: early but easier this way for now) */
- if (msg) {
- last_tx_tch_fn = l1s.next_time.fn;
- last_tx_tch_type |= TX_TYPE_TRAFFIC;
- l1s_compl_sched(L1_COMPL_TX_TCH);
- }
+ last_tx_tch_fn = l1s.next_time.fn;
+ last_tx_tch_type |= TX_TYPE_TRAFFIC;
+ l1s_compl_sched(L1_COMPL_TX_TCH);
/* Free msg now that we're done with it */
- if (msg)
- msgb_free(msg);
+ msgb_free(msg);
}
skip_tx_traffic:
@@ -535,6 +523,12 @@ skip_tx_traffic:
);
l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0);
+ /* If transmission is off, use dummy task for DSP and do not open TX window. */
+ if ((l1s.tch_flags & L1CTL_TCH_FLAG_RXONLY)) {
+ dsp_load_tx_task(TCHD_DSP_TASK, 0, tsc); /* burst_id unused for TCH */
+ return 0;
+ }
+
dsp_load_tx_task(
dsp_task_iq_swap(TCHT_DSP_TASK, arfcn, 1),
0, tsc /* burst_id unused for TCH */
@@ -800,6 +794,10 @@ static int l1s_tch_a_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
);
l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0);
+ /* If transmission is off, schedule no task for DSP and do not open TX window. */
+ if ((l1s.tch_flags & L1CTL_TCH_FLAG_RXONLY))
+ return 0;
+
dsp_load_tx_task(
dsp_task_iq_swap(TCHA_DSP_TASK, arfcn, 1),
0, tsc /* burst_id unused for TCHA */
diff --git a/src/target/firmware/layer1/prim_tx_nb.c b/src/target/firmware/layer1/prim_tx_nb.c
index 86e8224c..cf4ea44b 100644
--- a/src/target/firmware/layer1/prim_tx_nb.c
+++ b/src/target/firmware/layer1/prim_tx_nb.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 <stdint.h>
diff --git a/src/target/firmware/layer1/prim_utils.c b/src/target/firmware/layer1/prim_utils.c
index 7beab5fe..f19200eb 100644
--- a/src/target/firmware/layer1/prim_utils.c
+++ b/src/target/firmware/layer1/prim_utils.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/rfch.c b/src/target/firmware/layer1/rfch.c
index 3c4e6764..4c34416f 100644
--- a/src/target/firmware/layer1/rfch.c
+++ b/src/target/firmware/layer1/rfch.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/sched_gsmtime.c b/src/target/firmware/layer1/sched_gsmtime.c
index 01e22ca3..aec041ec 100644
--- a/src/target/firmware/layer1/sched_gsmtime.c
+++ b/src/target/firmware/layer1/sched_gsmtime.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c
index 978546b5..1b7eb326 100644
--- a/src/target/firmware/layer1/sync.c
+++ b/src/target/firmware/layer1/sync.c
@@ -16,10 +16,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 <stdint.h>
diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c
index 88129922..44b88b22 100644
--- a/src/target/firmware/layer1/tdma_sched.c
+++ b/src/target/firmware/layer1/tdma_sched.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/layer1/toa.c b/src/target/firmware/layer1/toa.c
index 7d80d952..4c9fff8d 100644
--- a/src/target/firmware/layer1/toa.c
+++ b/src/target/firmware/layer1/toa.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 <stdint.h>
diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c
index f4e76c16..7baa1d35 100644
--- a/src/target/firmware/layer1/tpu_window.c
+++ b/src/target/firmware/layer1/tpu_window.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c
index 6bc8fede..df2adab1 100644
--- a/src/target/firmware/lib/console.c
+++ b/src/target/firmware/lib/console.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/rf/mt6139.c b/src/target/firmware/rf/mt6139.c
index d48e6529..fab36ba5 100644
--- a/src/target/firmware/rf/mt6139.c
+++ b/src/target/firmware/rf/mt6139.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c
index ec139282..5c995d64 100644
--- a/src/target/firmware/rf/trf6151.c
+++ b/src/target/firmware/rf/trf6151.c
@@ -14,10 +14,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 <stdint.h>
diff --git a/src/target/firmware/solve_envs.py b/src/target/firmware/solve_envs.py
index b0108971..da1b0e10 100755
--- a/src/target/firmware/solve_envs.py
+++ b/src/target/firmware/solve_envs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import sys
diff --git a/src/target/firmware/tiffs/init.c b/src/target/firmware/tiffs/init.c
index 378f8291..a6c38e3a 100644
--- a/src/target/firmware/tiffs/init.c
+++ b/src/target/firmware/tiffs/init.c
@@ -17,10 +17,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>
diff --git a/src/target/firmware/tiffs/readfile.c b/src/target/firmware/tiffs/readfile.c
index c712c437..c93b1067 100644
--- a/src/target/firmware/tiffs/readfile.c
+++ b/src/target/firmware/tiffs/readfile.c
@@ -17,10 +17,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>
diff --git a/src/target/trx_toolkit/app_common.py b/src/target/trx_toolkit/app_common.py
index 7f2f03c5..531d8dcf 100644
--- a/src/target/trx_toolkit/app_common.py
+++ b/src/target/trx_toolkit/app_common.py
@@ -16,10 +16,6 @@
# 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.
import logging as log
diff --git a/src/target/trx_toolkit/burst_fwd.py b/src/target/trx_toolkit/burst_fwd.py
index 2e9e97b5..69245317 100644
--- a/src/target/trx_toolkit/burst_fwd.py
+++ b/src/target/trx_toolkit/burst_fwd.py
@@ -17,10 +17,6 @@
# 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.
import logging as log
@@ -62,8 +58,6 @@ class BurstForwarder(TRXList):
# Check transceiver state
if not trx.running:
continue
- if rx_msg.tn not in trx.ts_list:
- continue
# Match Tx/Rx frequencies of the both transceivers
if trx.get_rx_freq(rx_msg.fn) != tx_freq:
diff --git a/src/target/trx_toolkit/burst_gen.py b/src/target/trx_toolkit/burst_gen.py
index f93d8685..22f72053 100755
--- a/src/target/trx_toolkit/burst_gen.py
+++ b/src/target/trx_toolkit/burst_gen.py
@@ -18,10 +18,6 @@
# 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.
APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
diff --git a/src/target/trx_toolkit/burst_send.py b/src/target/trx_toolkit/burst_send.py
index 4e165711..27f585e0 100755
--- a/src/target/trx_toolkit/burst_send.py
+++ b/src/target/trx_toolkit/burst_send.py
@@ -17,10 +17,6 @@
# 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.
APP_CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy <axilirator@gmail.com>")]
diff --git a/src/target/trx_toolkit/clck_gen.py b/src/target/trx_toolkit/clck_gen.py
index 51f04a82..427eb88f 100755
--- a/src/target/trx_toolkit/clck_gen.py
+++ b/src/target/trx_toolkit/clck_gen.py
@@ -17,10 +17,6 @@
# 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.
APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
diff --git a/src/target/trx_toolkit/codec.py b/src/target/trx_toolkit/codec.py
index 93875923..c5706009 100644
--- a/src/target/trx_toolkit/codec.py
+++ b/src/target/trx_toolkit/codec.py
@@ -21,10 +21,6 @@ Inspired by Pycrate and Scapy.
# 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.
from typing import Optional, Callable, Tuple, Any
import abc
diff --git a/src/target/trx_toolkit/ctrl_cmd.py b/src/target/trx_toolkit/ctrl_cmd.py
index 7a2bfacf..c6ac6d84 100755
--- a/src/target/trx_toolkit/ctrl_cmd.py
+++ b/src/target/trx_toolkit/ctrl_cmd.py
@@ -18,10 +18,6 @@
# 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.
APP_CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy <axilirator@gmail.com>")]
diff --git a/src/target/trx_toolkit/ctrl_if.py b/src/target/trx_toolkit/ctrl_if.py
index 4f440807..f9d30c19 100644
--- a/src/target/trx_toolkit/ctrl_if.py
+++ b/src/target/trx_toolkit/ctrl_if.py
@@ -17,12 +17,9 @@
# 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.
import logging as log
+import time
from udp_link import UDPLink
@@ -31,6 +28,9 @@ class CTRLInterface(UDPLink):
UDPLink.__init__(self, *udp_link_args)
log.debug("Init TRXC interface (%s)" % self.desc_link())
+ # Do not delay RSP messages by default
+ self.rsp_delay_ms = 0
+
def handle_rx(self):
# Read data from socket
data, remote = self.sock.recvfrom(128)
@@ -86,6 +86,9 @@ class CTRLInterface(UDPLink):
# Add the response signature, and join back to string
response = "RSP " + " ".join(request) + "\0"
+ # If configured, delay sending the RSP message
+ if self.rsp_delay_ms > 0:
+ time.sleep(self.rsp_delay_ms / 1000.0)
# Now we have something like "RSP TXTUNE 0 941600"
self.sendto(response, remote)
diff --git a/src/target/trx_toolkit/ctrl_if_trx.py b/src/target/trx_toolkit/ctrl_if_trx.py
index c03dcc60..e6fdaf1d 100644
--- a/src/target/trx_toolkit/ctrl_if_trx.py
+++ b/src/target/trx_toolkit/ctrl_if_trx.py
@@ -17,10 +17,6 @@
# 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.
import logging as log
@@ -111,10 +107,7 @@ class CTRLInterfaceTRX(CTRLInterface):
return -1
log.info("(%s) Starting transceiver..." % self.trx)
- self.trx.running = True
-
- # Notify transceiver about that
- self.trx.power_event_handler("POWERON")
+ self.trx.power_event_handler(poweron=True)
return 0
@@ -122,10 +115,7 @@ class CTRLInterfaceTRX(CTRLInterface):
log.debug("(%s) Recv POWEROFF cmd" % self.trx)
log.info("(%s) Stopping transceiver..." % self.trx)
- self.trx.running = False
-
- # Notify transceiver about that
- self.trx.power_event_handler("POWEROFF")
+ self.trx.power_event_handler(poweron=False)
return 0
@@ -144,32 +134,6 @@ class CTRLInterfaceTRX(CTRLInterface):
self.trx._tx_freq = int(request[1]) * 1000
return 0
- elif self.verify_cmd(request, "SETSLOT", 2):
- log.debug("(%s) Recv SETSLOT cmd" % self.trx)
-
- # Obtain TS index
- ts = int(request[1])
- if ts not in range(0, 8):
- log.error("(%s) TS index should be in "
- "range: 0..7" % self.trx)
- return -1
-
- # Parse TS type
- ts_type = int(request[2])
-
- # TS activation / deactivation
- # We don't care about ts_type
- if ts_type == 0:
- # Deactivate TS (remove from the list of active timeslots)
- if ts in self.trx.ts_list:
- self.trx.ts_list.remove(ts)
- else:
- # Activate TS (add to the list of active timeslots)
- if ts not in self.trx.ts_list:
- self.trx.ts_list.append(ts)
-
- return 0
-
# Power measurement
if self.verify_cmd(request, "MEASURE", 1):
log.debug("(%s) Recv MEASURE cmd" % self.trx)
diff --git a/src/target/trx_toolkit/data_dump.py b/src/target/trx_toolkit/data_dump.py
index 8510e2d0..8475ceb2 100644
--- a/src/target/trx_toolkit/data_dump.py
+++ b/src/target/trx_toolkit/data_dump.py
@@ -16,10 +16,6 @@
# 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.
import logging as log
import struct
diff --git a/src/target/trx_toolkit/data_if.py b/src/target/trx_toolkit/data_if.py
index 1cded9bb..8e808943 100644
--- a/src/target/trx_toolkit/data_if.py
+++ b/src/target/trx_toolkit/data_if.py
@@ -16,10 +16,6 @@
# 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.
import logging as log
diff --git a/src/target/trx_toolkit/data_msg.py b/src/target/trx_toolkit/data_msg.py
index 72e248da..898a4ae6 100644
--- a/src/target/trx_toolkit/data_msg.py
+++ b/src/target/trx_toolkit/data_msg.py
@@ -16,10 +16,6 @@
# 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.
import random
import struct
diff --git a/src/target/trx_toolkit/fake_pm.py b/src/target/trx_toolkit/fake_pm.py
index 93312125..205596a9 100644
--- a/src/target/trx_toolkit/fake_pm.py
+++ b/src/target/trx_toolkit/fake_pm.py
@@ -16,10 +16,6 @@
# 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.
from random import randint
diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py
index d519a9ae..0daecb40 100755
--- a/src/target/trx_toolkit/fake_trx.py
+++ b/src/target/trx_toolkit/fake_trx.py
@@ -17,10 +17,6 @@
# 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.
APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
@@ -374,6 +370,15 @@ class FakeTRX(Transceiver):
self.burst_drop_period = period
return 0
+ # Artificial delay for the TRXC interface
+ # Syntax: CMD FAKE_TRXC_DELAY <DELAY_MS>
+ elif self.ctrl_if.verify_cmd(request, "FAKE_TRXC_DELAY", 1):
+ log.debug("(%s) Recv FAKE_TRXC_DELAY cmd", self)
+
+ self.ctrl_if.rsp_delay_ms = int(request[1])
+ log.info("(%s) Artificial TRXC delay set to %d",
+ self, self.ctrl_if.rsp_delay_ms)
+
# Unhandled command
return None
@@ -401,34 +406,31 @@ class Application(ApplicationBase):
self.fake_pm.trx_list = self.trx_list
# Init TRX instance for BTS
- self.append_trx(self.argv.bts_addr,
- self.argv.bts_base_port, name = "BTS")
+ self.append_trx(self.argv.bts_addr, self.argv.bts_base_port, name = "BTS")
# Init TRX instance for BB
- self.append_trx(self.argv.bb_addr,
- self.argv.bb_base_port, name = "MS")
+ self.append_trx(self.argv.bb_addr, self.argv.bb_base_port, name = "MS", child_mgt = False)
# Additional transceivers (optional)
if self.argv.trx_list is not None:
for trx_def in self.argv.trx_list:
(name, addr, port, idx) = trx_def
- self.append_child_trx(addr, port, idx, name)
+ self.append_child_trx(addr, port, name = name, child_idx = idx)
# Burst forwarding between transceivers
self.burst_fwd = BurstForwarder(self.trx_list.trx_list)
log.info("Init complete")
- def append_trx(self, remote_addr, base_port, name = None):
+ def append_trx(self, remote_addr, base_port, **kwargs):
trx = FakeTRX(self.argv.trx_bind_addr, remote_addr, base_port,
- clck_gen = self.clck_gen, pwr_meas = self.fake_pm,
- name = name)
+ clck_gen = self.clck_gen, pwr_meas = self.fake_pm, **kwargs)
self.trx_list.add_trx(trx)
- def append_child_trx(self, remote_addr, base_port, child_idx, name = None):
- # Index 0 corresponds to the first transceiver
- if child_idx == 0:
- self.append_trx(remote_addr, base_port, name)
+ def append_child_trx(self, remote_addr, base_port, **kwargs):
+ child_idx = kwargs.get("child_idx", 0)
+ if child_idx == 0: # Index 0 indicates parent transceiver
+ self.append_trx(remote_addr, base_port, **kwargs)
return
# Find 'parent' transceiver for a new child
@@ -439,7 +441,7 @@ class Application(ApplicationBase):
# Allocate a new child
trx_child = FakeTRX(self.argv.trx_bind_addr, remote_addr, base_port,
- child_idx = child_idx, pwr_meas = self.fake_pm, name = name)
+ pwr_meas = self.fake_pm, **kwargs)
self.trx_list.add_trx(trx_child)
# Link a new 'child' with its 'parent'
diff --git a/src/target/trx_toolkit/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py
index 7c50245a..5f59ba6f 100644
--- a/src/target/trx_toolkit/gsm_shared.py
+++ b/src/target/trx_toolkit/gsm_shared.py
@@ -17,10 +17,6 @@
# 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.
from enum import Enum
diff --git a/src/target/trx_toolkit/rand_burst_gen.py b/src/target/trx_toolkit/rand_burst_gen.py
index b9846ab2..c474c759 100644
--- a/src/target/trx_toolkit/rand_burst_gen.py
+++ b/src/target/trx_toolkit/rand_burst_gen.py
@@ -17,10 +17,6 @@
# 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.
import random
diff --git a/src/target/trx_toolkit/test_codec.py b/src/target/trx_toolkit/test_codec.py
index ca2f773e..5049f525 100644
--- a/src/target/trx_toolkit/test_codec.py
+++ b/src/target/trx_toolkit/test_codec.py
@@ -19,10 +19,6 @@ Unit tests for declarative message codec.
# 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.
import unittest
import struct
diff --git a/src/target/trx_toolkit/test_data_dump.py b/src/target/trx_toolkit/test_data_dump.py
index f7b4fde4..67ff32b8 100644
--- a/src/target/trx_toolkit/test_data_dump.py
+++ b/src/target/trx_toolkit/test_data_dump.py
@@ -17,10 +17,6 @@
# 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.
import unittest
import tempfile
diff --git a/src/target/trx_toolkit/test_data_msg.py b/src/target/trx_toolkit/test_data_msg.py
index 24fda673..ec6c2ec1 100644
--- a/src/target/trx_toolkit/test_data_msg.py
+++ b/src/target/trx_toolkit/test_data_msg.py
@@ -17,10 +17,6 @@
# 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.
import unittest
diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py
index b48dffb9..ffd18abd 100644
--- a/src/target/trx_toolkit/transceiver.py
+++ b/src/target/trx_toolkit/transceiver.py
@@ -17,10 +17,6 @@
# 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.
import logging as log
@@ -47,16 +43,6 @@ class Transceiver:
NOTE: CLCK is not required for some L1 implementations, so it is optional.
- == Timeslot configuration
-
- Transceiver has a list of active (i.e. configured) TDMA timeslots.
- The L1 should configure a timeslot before sending or expecting any
- data on it. This is done by SETSLOT control command, which also
- indicates an associated channel combination (see GSM TS 05.02).
-
- NOTE: we don't store the associated channel combinations,
- as they are only useful for burst detection and demodulation.
-
== Child transceivers
A BTS can (optionally) have more than one transceiver. In this case
@@ -69,8 +55,9 @@ class Transceiver:
(trx_2) ctrl=5705, data=5706.
...
- As soon as the first transceiver is powered on / off,
- all child transceivers are also powered on / off.
+ By default, powering on/off a parent transceiver (child_idx=0) will
+ automatically power on/off its child transceivers (if any). This
+ behavior can be disabled by setting "child_mgt" param to False.
== Clock distribution (optional)
@@ -124,42 +111,42 @@ class Transceiver:
"""
- def __init__(self, bind_addr, remote_addr, base_port, name = None,
- child_idx = 0, clck_gen = None, pwr_meas = None):
+ def __init__(self, bind_addr, remote_addr, base_port, **kwargs):
# Connection info
self.remote_addr = remote_addr
self.bind_addr = bind_addr
self.base_port = base_port
- self.child_idx = child_idx
+ self.child_idx = kwargs.get("child_idx", 0)
+ self.child_mgt = kwargs.get("child_mgt", True)
# Meta info
- self.name = name
+ self.name = kwargs.get("name", None)
log.info("Init transceiver '%s'" % self)
# Child transceiver cannot have its own clock
- if clck_gen is not None and child_idx > 0:
+ self.clck_gen = kwargs.get("clck_gen", None)
+ if self.clck_gen is not None and self.child_idx > 0:
raise TypeError("Child transceiver cannot have its own clock")
# Init DATA interface
self.data_if = DATAInterface(
- remote_addr, base_port + child_idx * 2 + 102,
- bind_addr, base_port + child_idx * 2 + 2)
+ remote_addr, base_port + self.child_idx * 2 + 102,
+ bind_addr, base_port + self.child_idx * 2 + 2)
# Init CTRL interface
self.ctrl_if = CTRLInterfaceTRX(self,
- remote_addr, base_port + child_idx * 2 + 101,
- bind_addr, base_port + child_idx * 2 + 1)
+ remote_addr, base_port + self.child_idx * 2 + 101,
+ bind_addr, base_port + self.child_idx * 2 + 1)
# Init optional CLCK interface
- self.clck_gen = clck_gen
- if clck_gen is not None:
+ if self.clck_gen is not None:
self.clck_if = UDPLink(
remote_addr, base_port + 100,
bind_addr, base_port)
# Optional Power Measurement interface
- self.pwr_meas = pwr_meas
+ self.pwr_meas = kwargs.get("pwr_meas", None)
# Internal state
self.running = False
@@ -171,9 +158,6 @@ class Transceiver:
# Frequency hopping parameters (set by CTRL)
self.fh = None
- # List of active (configured) timeslots
- self.ts_list = []
-
# List of child transceivers
self.child_trx_list = TRXList()
@@ -226,19 +210,18 @@ class Transceiver:
def ctrl_cmd_handler(self, request):
return None
- def power_event_handler(self, event):
- # Update child transceivers
- for trx in self.child_trx_list.trx_list:
- if event == "POWERON":
- trx.running = True
- elif event == "POWEROFF":
- trx.running = False
+ def power_event_handler(self, poweron: bool) -> None:
+ # If self.child_mgt is True, automatically power on/off children
+ if self.child_mgt and self.child_idx == 0:
+ trx_list = [self, *self.child_trx_list.trx_list]
+ else:
+ trx_list = [self]
+ # Update self and optionally child transceivers
+ for trx in trx_list:
+ trx.running = poweron
+ if not poweron:
trx.disable_fh()
- # Reset frequency hopping parameters
- if event == "POWEROFF":
- self.disable_fh()
-
# Trigger clock generator if required
if self.clck_gen is not None:
clck_links = self.clck_gen.clck_links
@@ -268,13 +251,6 @@ class Transceiver:
"is not running => dropping..." % (self, msg.desc_hdr()))
return None
- # Make sure that indicated timeslot is configured
- # Pass PDUs without burst bits, they will be sent as NOPE.ind
- if msg.tn not in self.ts_list and msg.burst:
- log.warning("(%s) RX TRXD message (%s), but timeslot is not "
- "configured => dropping..." % (self, msg.desc_hdr()))
- return None
-
return msg
def handle_data_msg(self, msg):
diff --git a/src/target/trx_toolkit/trx_list.py b/src/target/trx_toolkit/trx_list.py
index 0a55cc64..c476a7d0 100644
--- a/src/target/trx_toolkit/trx_list.py
+++ b/src/target/trx_toolkit/trx_list.py
@@ -16,10 +16,6 @@
# 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.
class TRXList:
""" Transceiver list implementation.
diff --git a/src/target/trx_toolkit/trx_sniff.py b/src/target/trx_toolkit/trx_sniff.py
index 8b6f80c7..c91e3e0b 100755
--- a/src/target/trx_toolkit/trx_sniff.py
+++ b/src/target/trx_toolkit/trx_sniff.py
@@ -17,10 +17,6 @@
# 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.
APP_CR_HOLDERS = [("2018-2020", "Vadim Yanitskiy <axilirator@gmail.com>")]
diff --git a/src/target/trx_toolkit/trxd_proto.py b/src/target/trx_toolkit/trxd_proto.py
index 21fe31ea..a1bf1b53 100644
--- a/src/target/trx_toolkit/trxd_proto.py
+++ b/src/target/trx_toolkit/trxd_proto.py
@@ -20,10 +20,6 @@ TRXD PDU definitions based on declarative codec.
# 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.
import codec
diff --git a/src/target/trx_toolkit/udp_link.py b/src/target/trx_toolkit/udp_link.py
index 43a4815a..c3f14765 100644
--- a/src/target/trx_toolkit/udp_link.py
+++ b/src/target/trx_toolkit/udp_link.py
@@ -16,10 +16,6 @@
# 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.
import socket
diff --git a/src/target/ui-experiment/font.h b/src/target/ui-experiment/font.h
index 5dc6eae7..c75a0f88 100644
--- a/src/target/ui-experiment/font.h
+++ b/src/target/ui-experiment/font.h
Binary files differ