summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml1
-rwxr-xr-xcontrib/jenkins.sh34
-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.cfg23
-rw-r--r--doc/examples/mobile/multi_ms.cfg22
-rw-r--r--doc/examples/modem/modem.cfg24
-rw-r--r--doc/manuals/Makefile1
-rw-r--r--include/l1ctl_proto.h199
-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/README2
-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.h125
-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.h42
-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.h105
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h48
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/mncc.h39
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h35
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/settings.h125
-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.h13
-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.c34
-rw-r--r--src/host/layer23/src/common/l1ctl.c370
-rw-r--r--src/host/layer23/src/common/l1ctl_lapdm_glue.c13
-rw-r--r--src/host/layer23/src/common/l1l2_interface.c9
-rw-r--r--src/host/layer23/src/common/logging.c96
-rw-r--r--src/host/layer23/src/common/main.c223
-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.c6
-rw-r--r--src/host/layer23/src/common/settings.c (renamed from src/host/layer23/src/mobile/settings.c)143
-rw-r--r--src/host/layer23/src/common/sim.c14
-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.c678
-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.c38
-rw-r--r--src/host/layer23/src/misc/app_cbch_sniff.c76
-rw-r--r--src/host/layer23/src/misc/app_ccch_scan.c137
-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.c40
-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.c301
-rw-r--r--src/host/layer23/src/mobile/gapk_io.c547
-rw-r--r--src/host/layer23/src/mobile/gsm322.c781
-rw-r--r--src/host/layer23/src/mobile/gsm411_sms.c26
-rw-r--r--src/host/layer23/src/mobile/gsm414.c218
-rw-r--r--src/host/layer23/src/mobile/gsm44068_gcc_bcc.c1967
-rw-r--r--src/host/layer23/src/mobile/gsm480_ss.c112
-rw-r--r--src/host/layer23/src/mobile/gsm48_cc.c169
-rw-r--r--src/host/layer23/src/mobile/gsm48_mm.c1451
-rw-r--r--src/host/layer23/src/mobile/gsm48_rr.c2230
-rw-r--r--src/host/layer23/src/mobile/main.c186
-rw-r--r--src/host/layer23/src/mobile/mncc_sock.c44
-rw-r--r--src/host/layer23/src/mobile/mnccms.c641
-rw-r--r--src/host/layer23/src/mobile/primitives.c4
-rw-r--r--src/host/layer23/src/mobile/script_lua.c14
-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.c78
-rw-r--r--src/host/layer23/src/mobile/vty_interface.c2178
-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.c51
-rw-r--r--src/host/osmocon/osmoload.c4
-rw-r--r--src/host/osmocon/tpu_debug.c66
-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.c905
-rw-r--r--src/host/trxcon/l1ctl.h26
-rw-r--r--src/host/trxcon/l1ctl_link.c319
-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.c181
-rw-r--r--src/host/trxcon/sched_lchan_pdtch.c204
-rw-r--r--src/host/trxcon/sched_lchan_rach.c181
-rw-r--r--src/host/trxcon/sched_lchan_tchf.c297
-rw-r--r--src/host/trxcon/sched_lchan_tchh.c502
-rw-r--r--src/host/trxcon/sched_lchan_xcch.c213
-rw-r--r--src/host/trxcon/sched_mframe.c2101
-rw-r--r--src/host/trxcon/sched_prim.c611
-rw-r--r--src/host/trxcon/sched_trx.c704
-rw-r--r--src/host/trxcon/sched_trx.h369
-rw-r--r--src/host/trxcon/scheduler.h54
-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)374
-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)480
-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.c423
-rw-r--r--src/host/trxcon/src/trxcon_shim.c262
-rw-r--r--src/host/trxcon/trx_if.h80
-rw-r--r--src/host/trxcon/trxcon.c343
-rw-r--r--src/host/trxcon/trxcon.h20
-rw-r--r--src/host/virt_phy/configure.ac40
-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)32
-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)4
-rw-r--r--src/host/virt_phy/include/virtphy/l1ctl_sap.h80
-rw-r--r--src/host/virt_phy/src/Makefile.am31
-rw-r--r--src/host/virt_phy/src/gsmtapl1_if.c213
-rw-r--r--src/host/virt_phy/src/l1ctl_sap.c193
-rw-r--r--src/host/virt_phy/src/l1ctl_sock.c15
l---------src/host/virt_phy/src/l1gprs.c1
-rw-r--r--src/host/virt_phy/src/logging.c120
-rw-r--r--src/host/virt_phy/src/shared/osmo_mcast_sock.c12
-rw-r--r--src/host/virt_phy/src/shared/virtual_um.c83
-rw-r--r--src/host/virt_phy/src/virt_l1_model.c7
-rw-r--r--src/host/virt_phy/src/virt_l1_sched_simple.c19
-rw-r--r--src/host/virt_phy/src/virt_prim_data.c29
-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.c56
-rw-r--r--src/host/virt_phy/src/virt_prim_rach.c40
-rw-r--r--src/host/virt_phy/src/virt_prim_traffic.c39
-rw-r--r--src/host/virt_phy/src/virtphy.c52
-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/Makefile55
-rw-r--r--src/target/firmware/Makefile.inc12
-rw-r--r--src/target/firmware/abb/twl3025.c14
-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.c9
-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.c333
-rw-r--r--src/target/firmware/apps/rssi/main.c15
-rwxr-xr-xsrc/target/firmware/apps/simtest/main.c4
-rw-r--r--src/target/firmware/apps/snake_game/main.c517
-rw-r--r--src/target/firmware/battery/compal_e88.c5
-rw-r--r--src/target/firmware/board/common/readcal_tiffs.c75
-rw-r--r--src/target/firmware/board/common/tx_calchan.c206
-rw-r--r--src/target/firmware/board/compal/highram.lds2
-rw-r--r--src/target/firmware/board/compal/ram.lds2
-rw-r--r--src/target/firmware/board/compal/readcal_common.c120
-rw-r--r--src/target/firmware/board/compal/readcal_small.c26
-rw-r--r--src/target/firmware/board/compal/rf_power.c62
-rw-r--r--src/target/firmware/board/compal/rf_tables.c190
-rw-r--r--src/target/firmware/board/compal_e86/init.c4
-rw-r--r--src/target/firmware/board/compal_e86/tx_ramps.c431
-rwxr-xr-xsrc/target/firmware/board/compal_e88/init.c4
-rw-r--r--src/target/firmware/board/compal_e88/tx_ramps.c432
-rw-r--r--src/target/firmware/board/compal_e99/init.c4
-rw-r--r--src/target/firmware/board/compal_e99/readcal.c26
-rw-r--r--src/target/firmware/board/fcdev3b/init.c4
-rw-r--r--src/target/firmware/board/gta0x/afcparams.c49
-rw-r--r--src/target/firmware/board/gta0x/init.c4
-rw-r--r--src/target/firmware/board/gta0x/rf_power.c63
-rw-r--r--src/target/firmware/board/gta0x/rf_tables.c569
-rw-r--r--src/target/firmware/board/gta0x/rffe_gta0x_triband.c20
-rw-r--r--src/target/firmware/board/gtm900b/afcparams.c43
-rw-r--r--src/target/firmware/board/gtm900b/init.c103
-rw-r--r--src/target/firmware/board/gtm900b/rf_power.c63
-rw-r--r--src/target/firmware/board/gtm900b/rffe_gtm900b.c38
-rw-r--r--src/target/firmware/board/mediatek/ram.lds2
-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.c87
-rw-r--r--src/target/firmware/board/pirelli_dpl10/rf_power.c63
-rw-r--r--src/target/firmware/board/pirelli_dpl10/rf_tables.c593
-rw-r--r--src/target/firmware/board/se_j100/init.c4
-rw-r--r--src/target/firmware/board/se_j100/tx_ramps.c437
-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.c6
-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.c74
-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.c6
-rw-r--r--src/target/firmware/comm/sercomm.c6
-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.c67
-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.c6
-rw-r--r--src/target/firmware/fb/fb_s6b33b1x.c6
-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.c5
-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
-rwxr-xr-xsrc/target/firmware/include/abb/twl3025.h3
-rw-r--r--src/target/firmware/include/calypso/dsp_api.h12
-rw-r--r--src/target/firmware/include/calypso/du.h4
-rw-r--r--src/target/firmware/include/calypso/l1_environment.h6
-rwxr-xr-xsrc/target/firmware/include/calypso/sim.h6
-rw-r--r--src/target/firmware/include/comm/timer.h6
-rw-r--r--src/target/firmware/include/fb/fb_bw8.h9
-rw-r--r--src/target/firmware/include/fb/framebuffer.h8
-rw-r--r--src/target/firmware/include/layer1/apc.h10
-rw-r--r--src/target/firmware/include/layer1/async.h9
-rw-r--r--src/target/firmware/include/layer1/mframe_sched.h4
-rw-r--r--src/target/firmware/include/layer1/prim.h2
-rw-r--r--src/target/firmware/include/layer1/rfch.h2
-rw-r--r--src/target/firmware/include/layer1/sync.h8
-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/include/rf/readcal.h1
-rw-r--r--src/target/firmware/include/rf/txcal.h49
-rw-r--r--src/target/firmware/include/rf/vcxocal.h13
-rw-r--r--src/target/firmware/include/stdint.h2
-rw-r--r--src/target/firmware/layer1/Makefile2
-rw-r--r--src/target/firmware/layer1/afc.c15
-rw-r--r--src/target/firmware/layer1/agc.c4
-rw-r--r--src/target/firmware/layer1/apc.c57
-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.c85
-rw-r--r--src/target/firmware/layer1/mframe_sched.c57
-rw-r--r--src/target/firmware/layer1/prim_fbsb.c8
-rw-r--r--src/target/firmware/layer1/prim_freq.c9
-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.c266
-rw-r--r--src/target/firmware/layer1/prim_tx_nb.c4
-rw-r--r--src/target/firmware/layer1/prim_utils.c22
-rw-r--r--src/target/firmware/layer1/rfch.c23
-rw-r--r--src/target/firmware/layer1/sched_gsmtime.c4
-rw-r--r--src/target/firmware/layer1/sync.c69
-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/lib/vsprintf.c2
-rw-r--r--src/target/firmware/rf/mt6139.c4
-rw-r--r--src/target/firmware/rf/trf6151.c8
-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.py48
-rw-r--r--src/target/trx_toolkit/burst_fwd.py59
-rwxr-xr-xsrc/target/trx_toolkit/burst_gen.py12
-rwxr-xr-xsrc/target/trx_toolkit/burst_send.py15
-rwxr-xr-xsrc/target/trx_toolkit/clck_gen.py82
-rw-r--r--src/target/trx_toolkit/codec.py404
-rwxr-xr-xsrc/target/trx_toolkit/ctrl_cmd.py6
-rw-r--r--src/target/trx_toolkit/ctrl_if.py23
-rw-r--r--src/target/trx_toolkit/ctrl_if_trx.py104
-rw-r--r--src/target/trx_toolkit/data_dump.py188
-rw-r--r--src/target/trx_toolkit/data_if.py38
-rw-r--r--src/target/trx_toolkit/data_msg.py640
-rw-r--r--src/target/trx_toolkit/fake_pm.py14
-rwxr-xr-xsrc/target/trx_toolkit/fake_trx.py135
-rw-r--r--src/target/trx_toolkit/gsm_shared.py84
-rw-r--r--src/target/trx_toolkit/rand_burst_gen.py23
-rw-r--r--src/target/trx_toolkit/test_codec.py592
-rw-r--r--src/target/trx_toolkit/test_data_dump.py161
-rw-r--r--src/target/trx_toolkit/test_data_msg.py189
-rw-r--r--src/target/trx_toolkit/transceiver.py158
-rw-r--r--src/target/trx_toolkit/trx_list.py10
-rwxr-xr-xsrc/target/trx_toolkit/trx_sniff.py61
-rw-r--r--src/target/trx_toolkit/trxd_proto.py175
-rw-r--r--src/target/trx_toolkit/udp_link.py7
-rw-r--r--src/target/ui-experiment/font.hbin61204 -> 61076 bytes
-rwxr-xr-xsrc/target_dsp/calypso/dump2coff.py2
486 files changed, 38127 insertions, 15627 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 52c65661..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,24 +38,25 @@ 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
if [ "$WITH_MANUALS" = "1" ]; then
- osmo-build-dep.sh osmo-gsm-manuals
make -C "$base/doc/manuals"
make -C "$base/doc/manuals" check
@@ -62,9 +66,17 @@ 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
+# Build the firmware (against the local copy of libosmocore)
+cd "$base/src"
+make firmware
+
+# TRX Toolkit unit tests
+cd "$base/src/target/trx_toolkit"
+python3 -m unittest discover -v
+
osmo-clean-workspace.sh
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 cc816305..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,4 +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
+ 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 bef2406e..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,11 +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
+ 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
@@ -109,4 +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
+ 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 05d65deb..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.
@@ -149,11 +126,28 @@ struct l1ctl_ccch_mode_conf {
uint8_t padding[3];
} __attribute__((packed));
+/* 3GPP TS 44.014, section 5.1 (Calypso specific numbers) */
+enum l1ctl_tch_loop_mode {
+ L1CTL_TCH_LOOP_OPEN = 0x00,
+ L1CTL_TCH_LOOP_A = 0x01,
+ L1CTL_TCH_LOOP_B = 0x02,
+ L1CTL_TCH_LOOP_C = 0x03,
+ L1CTL_TCH_LOOP_D = 0x04,
+ L1CTL_TCH_LOOP_E = 0x05,
+ L1CTL_TCH_LOOP_F = 0x06,
+ L1CTL_TCH_LOOP_I = 0x07,
+};
+
/* TCH mode was changed */
struct l1ctl_tch_mode_conf {
uint8_t tch_mode; /* enum tch_mode */
uint8_t audio_mode;
- uint8_t padding[2];
+ uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */
+ 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 */
@@ -179,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
@@ -231,14 +216,22 @@ struct l1ctl_tch_mode_req {
#define AUDIO_RX_SPEAKER (1<<2)
#define AUDIO_RX_TRAFFIC_IND (1<<3)
uint8_t audio_mode;
- uint8_t padding[2];
+ uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */
+ 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));
@@ -278,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 {
@@ -359,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));
+
+/* 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));
- /* one USF for each TN, or 255 for invalid/unused */
- uint8_t usf[8];
+/* 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/README b/src/host/layer23/README
index dd598234..c52e7bc6 100644
--- a/src/host/layer23/README
+++ b/src/host/layer23/README
@@ -30,7 +30,7 @@ Layer3 calls rslms_recvmsg() with a msgb that has the msgb->l2h pointing to a
RSL header (struct abis_rsl_common_hdr).
There are utility functions like rslms_tx_rll_req() and rslms_tx_rsll_req_l3()
-for creating msgb's with the apropriate RSL/RLL headers.
+for creating msgb's with the appropriate RSL/RLL headers.
=== LAPDm ===
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 e4dbdedc..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);
+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 f843f271..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 */
-/* structure of all received system informations */
+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 d4caac99..4b20a1a1 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
@@ -1,12 +1,13 @@
#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 slection process (automatic mode) */
+/* 4.3.1.1 List of states for PLMN selection process (automatic mode) */
#define GSM322_A0_NULL 0
#define GSM322_A1_TRYING_RPLMN 1
#define GSM322_A2_ON_PLMN 2
@@ -15,7 +16,7 @@
#define GSM322_A5_HPLMN_SEARCH 5
#define GSM322_A6_NO_SIM 6
-/* 4.3.1.2 List of states for PLMN slection process (manual mode) */
+/* 4.3.1.2 List of states for PLMN selection process (manual mode) */
#define GSM322_M0_NULL 0
#define GSM322_M1_TRYING_RPLMN 1
#define GSM322_M2_ON_PLMN 2
@@ -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,14 +125,14 @@ 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 */
#define GSM322_CCCH_ST_IDLE 0 /* no connection */
-#define GSM322_CCCH_ST_INIT 1 /* initalized */
+#define GSM322_CCCH_ST_INIT 1 /* initialized */
#define GSM322_CCCH_ST_SYNC 2 /* got sync */
-#define GSM322_CCCH_ST_DATA 3 /* receiveing data */
+#define GSM322_CCCH_ST_DATA 3 /* receiving data */
/* neighbour cell info list entry */
struct gsm322_neighbour {
@@ -144,7 +145,7 @@ struct gsm322_neighbour {
time_t when; /* when did we sync / read */
int16_t rxlev_sum_dbm; /* sum of received levels */
uint8_t rxlev_count; /* number of received levels */
- int8_t rla_c_dbm; /* average of the reveive level */
+ int8_t rla_c_dbm; /* average of the receive level */
uint8_t c12_valid; /* both C1 and C2 are calculated */
int16_t c1, c2, crh;
uint8_t checked_for_resel;
@@ -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 6e9c197c..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,19 +54,67 @@
#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 {
- int 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 */
#define GSM48_MMR_REG_REQ 0x01
@@ -71,10 +124,9 @@ struct gsm48_mmxx_hdr {
/* MMR-SAP header */
struct gsm48_mmr {
- int msg_type;
-
+ uint8_t msg_type;
uint8_t cause;
-};
+} __attribute__((packed));
/* GSM 04.07 9.2.1 */
#define GSM48_MMXX_ST_IDLE 0
@@ -134,13 +186,26 @@ 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 */
#define GSM_T3210_MS 20, 0
@@ -185,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 */
@@ -197,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 */
@@ -204,7 +277,7 @@ struct gsm48_mm_conn {
struct llist_head list;
struct gsm48_mmlayer *mm;
- /* ref and type form a unique tupple */
+ /* ref and protocol form a unique tuple */
uint32_t ref; /* reference to trans */
uint8_t protocol;
uint8_t transaction_id;
@@ -213,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 6996ff35..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 */
@@ -188,9 +212,27 @@ struct gsm48_rrlayer {
/* audio flow */
uint8_t audio_mode;
+ /* 3GPP TS 44.014 TCH test loop mode (L1CTL specific format) */
+ uint8_t tch_loop_mode;
+
/* 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);
@@ -209,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 8ec9358d..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 */
@@ -58,7 +35,7 @@ struct gsm_call {
#define MNCC_SETUP_CNF 0x0104
#define MNCC_SETUP_COMPL_REQ 0x0105
#define MNCC_SETUP_COMPL_IND 0x0106
-/* MNCC_REJ_* is perfomed via MNCC_REL_* */
+/* MNCC_REJ_* is performed via MNCC_REL_* */
#define MNCC_CALL_CONF_IND 0x0107
#define MNCC_CALL_PROC_REQ 0x0108
#define MNCC_PROGRESS_REQ 0x0109
@@ -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;
@@ -136,7 +118,7 @@ struct gsm_mncc {
/* which fields are present */
uint32_t fields;
- /* data derived informations (MNCC_F_ based) */
+ /* data derived information (MNCC_F_ based) */
struct gsm_mncc_bearer_cap bearer_cap;
struct gsm_mncc_number called;
struct gsm_mncc_number calling;
@@ -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 4e5d5a19..00000000
--- a/src/host/layer23/include/osmocom/bb/mobile/settings.h
+++ /dev/null
@@ -1,125 +0,0 @@
-#ifndef _settings_h
-#define _settings_h
-
-#define MOB_C7_DEFLT_ANY_TIMEOUT 30
-
-struct gsm_settings {
- char layer2_socket_path[128];
- char sap_socket_path[128];
-
- /* 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 3bec1139..f03012e5 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/vty.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/vty.h
@@ -6,15 +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,
+ 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 35ee4167..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>
@@ -82,7 +79,11 @@ int osmo_gpsd_cb(struct osmo_fd *bfd, unsigned int what)
g.valid = 0;
/* gps is offline */
+#if GPSD_API_MAJOR_VERSION >= 9 && GPSD_API_MINOR_VERSION >= 0
+ if (gdata->online.tv_sec || gdata->online.tv_nsec)
+#else
if (gdata->online)
+#endif
goto gps_not_ready;
#if GPSD_API_MAJOR_VERSION >= 5
@@ -102,7 +103,11 @@ int osmo_gpsd_cb(struct osmo_fd *bfd, unsigned int what)
/* data are valid */
if (gdata->set & LATLON_SET) {
g.valid = 1;
+#if GPSD_API_MAJOR_VERSION >= 9 && GPSD_API_MINOR_VERSION >= 0
+ g.gmt = gdata->fix.time.tv_sec;
+#else
g.gmt = gdata->fix.time;
+#endif
tm = localtime(&g.gmt);
diff = time(NULL) - g.gmt;
g.latitude = gdata->fix.latitude;
@@ -129,10 +134,6 @@ int osmo_gpsd_open(void)
{
LOGP(DGPS, LOGL_INFO, "Connecting to gpsd at '%s:%s'\n", g.gpsd_host, g.gpsd_port);
- gps_bfd.data = NULL;
- gps_bfd.when = BSC_FD_READ;
- gps_bfd.cb = osmo_gpsd_cb;
-
#if GPSD_API_MAJOR_VERSION >= 5
if (gps_open(g.gpsd_host, g.gpsd_port, &_gdata) == -1)
gdata = NULL;
@@ -145,15 +146,15 @@ int osmo_gpsd_open(void)
LOGP(DGPS, LOGL_ERROR, "Can't connect to gpsd\n");
return -1;
}
- gps_bfd.fd = gdata->gps_fd;
- if (gps_bfd.fd < 0)
- return gps_bfd.fd;
+ if (gdata->gps_fd < 0)
+ return gdata->gps_fd;
if (gps_stream(gdata, WATCH_ENABLE, NULL) == -1) {
LOGP(DGPS, LOGL_ERROR, "Error in gps_stream()\n");
return -1;
}
+ osmo_fd_setup(&gps_bfd, gdata->gps_fd, OSMO_FD_READ, osmo_gpsd_cb, NULL, 0);
osmo_fd_register(&gps_bfd);
return 0;
@@ -312,18 +313,17 @@ int osmo_serialgps_cb(struct osmo_fd *bfd, unsigned int what)
int osmo_serialgps_open(void)
{
int baud = 0;
+ int fd;
if (gps_bfd.fd > 0)
return 0;
LOGP(DGPS, LOGL_INFO, "Open GPS device '%s'\n", g.device);
- gps_bfd.data = NULL;
- gps_bfd.when = BSC_FD_READ;
- gps_bfd.cb = osmo_serialgps_cb;
- gps_bfd.fd = open(g.device, O_RDONLY);
- if (gps_bfd.fd < 0)
- return gps_bfd.fd;
+ fd = open(g.device, O_RDONLY);
+ if (fd < 0)
+ return fd;
+ osmo_fd_setup(&gps_bfd, fd, OSMO_FD_READ, osmo_serialgps_cb, NULL, 0);
switch (g.baud) {
case 4800:
diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c
index 5d6d9c0c..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,14 +37,63 @@
#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;
+/* determine the CCCH block number based on the frame number */
+static unsigned int fn2ccch_block(uint32_t fn)
+{
+ 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;
+}
+
+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_rsl2gsmtap2(rsl_chantype, link_id, false);
+ if (ret != GSMTAP_CHANNEL_PCH)
+ return ret;
+
+ if (fn2ccch_block(fn) >= num_agch)
+ return GSMTAP_CHANNEL_PCH;
+ return GSMTAP_CHANNEL_AGCH;
+}
+
+static const uint8_t fill_frame[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B
+};
+
+/* Paging Request 1 with "no identity" content, i.e. empty/dummy paging */
+static const uint8_t paging_fill[GSM_MACBLOCK_LEN] = {
+ 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b };
+
+static bool is_fill_frame(uint8_t chan_type, const uint8_t *data)
+{
+ switch (chan_type) {
+ case GSMTAP_CHANNEL_AGCH:
+ if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN))
+ return true;
+ break;
+ case GSMTAP_CHANNEL_PCH:
+ if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN))
+ return true;
+ break;
+ /* don't use 'default' case here as the above only conditionally return true */
+ }
+ return false;
+}
static struct msgb *osmo_l1_alloc(uint8_t msg_type)
{
@@ -63,7 +108,7 @@ static struct msgb *osmo_l1_alloc(uint8_t msg_type)
msg->l1h = msgb_put(msg, sizeof(*l1h));
l1h = (struct l1ctl_hdr *) msg->l1h;
l1h->msg_type = msg_type;
-
+
return msg;
}
@@ -145,6 +190,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
struct rx_meas_stat *meas = &ms->meas;
uint8_t chan_type, chan_ts, chan_ss;
uint8_t gsmtap_chan_type;
+ uint8_t bs_ag_blks_res;
struct gsm_time tm;
if (msgb_l1len(msg) < sizeof(*dl)) {
@@ -159,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,
@@ -229,11 +281,31 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- /* send CCCH data via GSMTAP */
- gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id);
- gsmtap_send(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));
+ /* May not be initialized in some applications (e.g. ccch_scan) */
+ if (ms->cellsel.si != NULL)
+ bs_ag_blks_res = ms->cellsel.si->bs_ag_blks_res;
+ else /* fall-back to 1 (this is what OsmoBTS does) */
+ bs_ag_blks_res = 1;
+
+ gsmtap_chan_type = chantype_rsl2gsmtap_ext(chan_type, dl->link_id, tm.fn, bs_ag_blks_res);
+ /* don't log fill frames via GSMTAP; they serve no purpose other than
+ * to clog up your logs */
+ if (!is_fill_frame(gsmtap_chan_type, ccch->data)) {
+ /* send CCCH data via GSMTAP */
+ 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));
+ }
+
+ /* Do not pass PDCH and CBCH frames to LAPDm */
+ switch (chan_type) {
+ case RSL_CHAN_OSMO_PDCH:
+ case RSL_CHAN_OSMO_CBCH4:
+ case RSL_CHAN_OSMO_CBCH8:
+ /* TODO: pass directly to l23 application */
+ msgb_free(msg);
+ return 0;
+ }
/* determine LAPDm entity based on SACCH or not */
if (dl->link_id & 0x40)
@@ -245,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);
@@ -284,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)));
@@ -296,10 +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, 0|0x4000, 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));
@@ -365,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)
+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;
@@ -380,6 +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);
}
@@ -431,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;
@@ -442,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;
@@ -478,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;
@@ -513,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);
}
@@ -630,13 +714,10 @@ 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);
-
+
return 0;
}
@@ -751,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);
@@ -762,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)) {
@@ -779,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)
@@ -800,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 */
@@ -808,20 +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));
-
- if ((frame[0] >> 4) != 0xd) {
- LOGP(DL1C, LOGL_ERROR, "Traffic Request has incorrect magic "
- "(%u != 0xd)\n", frame[0] >> 4);
- msgb_free(msg);
- return -EINVAL;
- }
-
-// 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));
@@ -884,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)
{
@@ -952,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 c07b0a1d..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>
@@ -62,6 +60,7 @@ static int layer2_read(struct osmo_fd *fd)
if (rc >= 0)
rc = -EIO;
layer2_close((struct osmocom_ms *) fd->data);
+ exit(102);
return rc;
}
@@ -127,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 8abd3f79..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;
}
}
@@ -208,21 +183,58 @@ void sighandler(int sigset)
if (sigset == SIGHUP || sigset == SIGPIPE)
return;
- fprintf(stderr, "Signal %d recevied.\n", 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 c3d202f6..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>
@@ -177,7 +173,7 @@ void sap_msgb_add_param(struct msgb *msg,
* \param[in] sap_msg pointer to SAP message header
* \param[in] param_type parameter type (see sap_param_type enum)
* \param[out] param_len parameter length (if found)
- * \returns pointer to a given parameter withing the message, NULL otherwise
+ * \returns pointer to a given parameter within the message, NULL otherwise
*/
struct sap_param *sap_get_param(const struct sap_message *sap_msg,
enum sap_param_type param_type, uint16_t *param_len)
diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/common/settings.c
index 388c7547..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,6 +45,26 @@ int gsm_settings_init(struct osmocom_ms *ms)
strcpy(set->layer2_socket_path, layer2_socket_path);
strcpy(set->sap_socket_path, sap_socket_path);
+ /* 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;
@@ -52,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;
@@ -84,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;
@@ -97,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;
}
@@ -148,9 +197,6 @@ int gsm_settings_exit(struct osmocom_ms *ms)
llist_del(&abbrev->list);
talloc_free(abbrev);
}
-
- script_lua_close(ms);
-
return 0;
}
@@ -194,3 +240,72 @@ int gsm_random_imei(struct gsm_settings *set)
return 0;
}
+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 ed7f54a9..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);
}
}
@@ -1132,7 +1128,7 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
case SIM_JOB_INCREASE:
if (length != 4) {
LOGP(DSIM, LOGL_ERROR, "expecting uint32_t as "
- "value lenght, but got %d bytes\n",
+ "value length, but got %d bytes\n",
length);
goto request_error;
}
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 b42bd653..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,34 +50,51 @@ 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",
gsm_print_arfcn(arfcn),
(refer_pcs) ? "PCS (1900)" : "DCS (1800)");
- print(priv, "Available SYSTEM INFORMATIONS =");
+ print(priv, "Available SYSTEM INFORMATION =");
if (s->si1)
print(priv, " 1");
if (s->si2)
@@ -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 22e6f1b9..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)
{
- /* don't do layer3_init() as we don't want an actualy L3 */
+ 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 8256eaf6..f83815ef 100644
--- a/src/host/layer23/src/misc/app_cbch_sniff.c
+++ b/src/host/layer23/src/misc/app_cbch_sniff.c
@@ -16,31 +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 <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)
{
+ uint8_t chan_nr;
+
if (!s->si1 || !s->si4)
return 0;
if (!s->chan_nr) {
@@ -48,6 +52,13 @@ static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s)
return 0;
}
+ /* Convert received channel number to Osmocom specific one;
+ * this way the layer1 can activate proper CBCH task. */
+ if (s->chan_nr != RSL_CHAN_SDCCH4_ACCH)
+ chan_nr = RSL_CHAN_OSMO_CBCH8 | (s->chan_nr & 0x07);
+ else
+ chan_nr = RSL_CHAN_OSMO_CBCH4;
+
if (s->h) {
LOGP(DRR, LOGL_INFO, "chan_nr = 0x%02x TSC = %d MAIO = %d "
"HSN = %d hseq (%d): %s\n",
@@ -56,13 +67,13 @@ static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s)
osmo_hexdump((unsigned char *) s->hopping, s->hopp_len * 2));
return l1ctl_tx_dm_est_req_h1(ms,
s->maio, s->hsn, s->hopping, s->hopp_len,
- s->chan_nr, s->tsc,
- GSM48_CMODE_SIGN, 0);
+ chan_nr, s->tsc,
+ 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,
- s->chan_nr, s->tsc, GSM48_CMODE_SIGN, 0);
+ chan_nr, s->tsc, GSM48_CMODE_SIGN, 0, 0);
}
}
@@ -104,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);
@@ -177,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 actualy 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 88a2bef3..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;
@@ -166,11 +166,16 @@ static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms)
struct gsm48_imm_ass *ia = msgb_l3(msg);
uint8_t ch_type, ch_subch, ch_ts;
- /* Discard packet TBF assignement */
+ /* Discard packet TBF assignment */
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;
}
@@ -409,6 +400,13 @@ int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms)
struct gsm48_system_information_type_header *sih = msgb_l3(msg);
int rc = 0;
+ /* 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;
@@ -482,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;
+ }
-int l23_app_init(struct osmocom_ms *ms)
+ l1ctl_tx_reset_req(app_state.ms, L1CTL_RES_T_FULL);
+ return 0;
+}
+
+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 7340dcb9..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 */
@@ -438,7 +440,7 @@ static int ta_result(uint8_t ta)
return 0;
}
-/* match request reference agains request */
+/* match request reference against request */
static int match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref)
{
uint8_t ia_t1, ia_t2, ia_t3;
@@ -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 4e80e4ea..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 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 f0f118b6..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,11 +143,10 @@ 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) {
- LOGP(DMOB, LOGL_NOTICE, "MS '%s' has been resetted\n", ms->name);
+ LOGP(DMOB, LOGL_NOTICE, "MS '%s' has been reset\n", ms->name);
ms->shutdown = MS_SHUTDOWN_COMPL;
break;
}
@@ -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 56dd236e..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);
@@ -156,7 +155,7 @@ static int gsm322_nb_meas_ind(struct osmocom_ms *ms, uint16_t arfcn,
*
* * subscr->plmn_list
*
- * The "PLMN Selector list" stores prefered networks to select during PLMN
+ * The "PLMN Selector list" stores preferred networks to select during PLMN
* search process. This list is also stored in the SIM.
*
* * subscr->plmn_na
@@ -172,7 +171,7 @@ static int gsm322_nb_meas_ind(struct osmocom_ms *ms, uint16_t arfcn,
*
* * cs->list[1024+299]
*
- * This list stores measurements and cell informations during cell selection
+ * This list stores measurements and cell information during cell selection
* process. It can be used to speed up repeated cell selection.
*
* * cs->ba_list
@@ -201,7 +200,7 @@ static int gsm322_nb_meas_ind(struct osmocom_ms *ms, uint16_t arfcn,
* the BCCH data after 5 minutes. This timer is also used if sync or read
* fails.
*
- * The C1 and C2 criterion is calculated for the currently monitored neigbour
+ * The C1 and C2 criterion is calculated for the currently monitored neighbour
* cells. During this process, a better neighbour cell will trigger cell
* re-selection.
*
@@ -427,14 +426,14 @@ static int16_t calculate_c2(int16_t c1, int serving, int last_serving,
return c2;
}
- /* penatly time reached */
+ /* penalty time reached */
if (t >= (penalty_time + 1) * 20) {
LOGP(DNB, LOGL_INFO, "C2 = C1 + CELL_RESELECT_OFFSET (%d) = %d "
"(PENALTY_TIME reached)\n", cell_resel_off, c2);
return c2;
}
- /* penalty time not reached, substract temporary offset */
+ /* penalty time not reached, subtract temporary offset */
if (temp_offset < 7)
c2 -= temp_offset * 10;
else
@@ -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;
}
@@ -975,7 +962,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms)
entries--;
}
- /* move ohter PLMN in decreasing order */
+ /* move other PLMN in decreasing order */
while(1) {
found = NULL;
llist_for_each_entry(temp, &temp_list, entry) {
@@ -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
@@ -1047,7 +1033,7 @@ static int gsm322_a_go_wait_for_plmns(struct osmocom_ms *ms, struct msgb *msg)
new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
- /* we must forward this, otherwhise "Any cell selection"
+ /* we must forward this, otherwise "Any cell selection"
* will not start automatically.
*/
nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
@@ -1070,41 +1056,37 @@ 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
- * no cell ist available. */
+ * no cell is available. */
if (found < 0) {
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,47 +1489,43 @@ 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 */
new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
- /* we must forward this, otherwhise "Any cell selection"
+ /* we must forward this, otherwise "Any cell selection"
* will not start automatically.
* this way we get back to the last PLMN, in case we gained
* our coverage back.
@@ -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;
@@ -1847,7 +1814,7 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc,
if (cs->state == GSM322_C2_STORED_CELL_SEL
|| cs->state == GSM322_C5_CHOOSE_CELL)
mask |= GSM322_CS_FLAG_BA;
- flags = mask; /* all masked flags are requied */
+ flags = mask; /* all masked flags are required */
/* loop through all scanned frequencies and select cell.
* if an index is given (arfci), we just check this cell only */
@@ -1860,12 +1827,12 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc,
cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
s = cs->list[i].sysinfo;
- /* channel has no informations for us */
+ /* channel has no information for us */
if (!s || (cs->list[i].flags & mask) != flags) {
continue;
}
- /* check C1 criteria not fullfilled */
+ /* check C1 criteria not fulfilled */
// TODO: class 3 DCS mobile
gsm_arfcn2band_rc(index2arfcn(i), &band);
class = class_of_band(ms, band);
@@ -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;
@@ -2195,7 +2151,7 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
if (cs->state == GSM322_C2_STORED_CELL_SEL
|| cs->state == GSM322_C5_CHOOSE_CELL)
mask |= GSM322_CS_FLAG_BA;
- flags = mask; /* all masked flags are requied */
+ flags = mask; /* all masked flags are required */
for (i = 0; i <= 1023+299; i++) {
j = 0; /* make gcc happy */
if (!ms->settings.skip_max_per_band) {
@@ -2249,7 +2205,7 @@ static int gsm322_cs_scan(struct osmocom_ms *ms)
}
/* NOTE: We might already have system information from previous
- * scan. But we need recent informations, so we scan again!
+ * scan. But we need recent information, so we scan again!
*/
/* Tune to frequency for a while, to receive broadcasts. */
@@ -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 */
@@ -2462,13 +2413,14 @@ indicate_plmn_avail:
return 0;
}
-/* process system information when returing to idle mode */
+/* process system information when returning to idle mode */
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));
}
}
@@ -2517,22 +2468,22 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
return ba;
}
-/* store BA whenever a system informations changes */
+/* store BA whenever a system information changes */
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));
}
@@ -2582,12 +2533,12 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- /* Store BA if we have full system info about cells and neigbor cells.
+ /* Store BA if we have full system info about cells and neighbor cells.
* Depending on the extended bit in the channel description,
- * we require more or less system informations about neighbor cells
+ * 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
@@ -2600,7 +2551,7 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
&& s->nb_ext_ind_si2bis)))
gsm322_store_ba_list(cs, s);
- /* update sel_si, if all relevant system informations received */
+ /* update sel_si, if all relevant system information received */
if (s->si1 && s->si2 && s->si3
&& (!s->nb_ext_ind_si2
|| (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
@@ -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 */
@@ -2635,6 +2586,8 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
if (cs->list[cs->arfci].sysinfo) {
LOGP(DCS, LOGL_DEBUG, "free sysinfo arfcn=%s\n",
gsm_print_arfcn(cs->arfcn));
+ if (cs->si == cs->list[cs->arfci].sysinfo)
+ cs->si = NULL;
talloc_free(cs->list[cs->arfci].sysinfo);
cs->list[cs->arfci].sysinfo = NULL;
}
@@ -2655,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;
}
@@ -2696,12 +2648,12 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- /* Store BA if we have full system info about cells and neigbor cells.
+ /* Store BA if we have full system info about cells and neighbor cells.
* Depending on the extended bit in the channel description,
- * we require more or less system informations about neighbor cells
+ * 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
@@ -2712,7 +2664,7 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
&& (!s->si2ter_ind || s->si2ter))
gsm322_store_ba_list(cs, s);
- /* all relevant system informations received */
+ /* all relevant system information received */
if (s->si1 && s->si2 && s->si3
&& (!s->nb_ext_ind_si2 || s->si2bis)
&& (!s->si2ter_ind || s->si2ter)) {
@@ -2752,6 +2704,8 @@ static void gsm322_cs_timeout(void *arg)
if (cs->list[cs->arfci].sysinfo) {
LOGP(DCS, LOGL_DEBUG, "free sysinfo arfcn=%s\n",
gsm_print_arfcn(cs->arfcn));
+ if (cs->si == cs->list[cs->arfci].sysinfo)
+ cs->si = NULL;
talloc_free(cs->list[cs->arfci].sysinfo);
cs->list[cs->arfci].sysinfo = NULL;
}
@@ -2919,6 +2873,8 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
cs->list[i].flags &= ~GSM322_CS_FLAG_SYSINFO;
LOGP(DCS, LOGL_DEBUG, "free sysinfo ARFCN=%s\n",
gsm_print_arfcn(index2arfcn(i)));
+ if (cs->si == cs->list[i].sysinfo)
+ cs->si = NULL;
talloc_free(cs->list[i].sysinfo);
cs->list[i].sysinfo = NULL;
}
@@ -3089,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);
@@ -3108,7 +3064,7 @@ static void gsm322_cs_loss(void *arg)
/* keep cell info for re-selection */
gsm48_rr_los(ms);
- /* be shure that nothing else is done after here
+ /* be sure that nothing else is done after here
* because the function call above may cause
* to return from idle state and trigger cell re-sel.
*/
@@ -3220,7 +3176,7 @@ static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms,
return gsm322_cs_powerscan(ms);
}
-/* start noraml cell selection */
+/* start normal cell selection */
static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm322_cellsel *cs = &ms->cellsel;
@@ -3265,7 +3221,7 @@ static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
/* indicate to MM that we lost coverage.
* this is the only case where we really have no coverage.
- * we tell MM, so it will enter the "No Cell Avaiable" state. */
+ * we tell MM, so it will enter the "No Cell Available" state. */
if (msg_type == GSM322_EVENT_NO_CELL_FOUND) {
struct msgb *nmsg;
@@ -3281,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);
@@ -3325,7 +3281,7 @@ static int gsm322_c_sim_remove(struct osmocom_ms *ms, struct msgb *msg)
return gsm322_c_any_cell_sel(ms, msg);
}
-/* start noraml cell re-selection */
+/* start normal cell re-selection */
static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm322_cellsel *cs = &ms->cellsel;
@@ -3383,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)
@@ -3410,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.
@@ -3423,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
@@ -3456,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;
@@ -3490,7 +3443,7 @@ struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
if (lower == higher)
break;
lower++;
- /* wrap arround, only if not PCS */
+ /* wrap around, only if not PCS */
if (lower == 1024)
lower = 0;
}
@@ -3520,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);
}
}
@@ -3622,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");
@@ -3829,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;
}
@@ -4148,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));
}
@@ -4175,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;
@@ -4183,12 +4135,12 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
/* check if we have successfully read BCCH */
if (!s || nb->state != GSM322_NB_SYSINFO) {
LOGP(DNB, LOGL_INFO, "Skip cell: There are no system "
- "informations available.\n");
+ "information available.\n");
if (ms->rrlayer.monitor) {
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));
@@ -4201,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),
@@ -4214,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;
@@ -4234,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));
}
@@ -4256,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;
}
@@ -4310,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) {
@@ -4356,7 +4309,7 @@ static int gsm322_nb_scan(struct osmocom_ms *ms)
nb->c2);
/* track which cells have been checked do far */
if (nb->checked_for_resel) {
- LOGP(DCS, LOGL_INFO, "Skip cell: alredy tried to "
+ LOGP(DCS, LOGL_INFO, "Skip cell: already tried to "
"select.\n");
goto cont;
}
@@ -4417,7 +4370,7 @@ no_cell_found:
nb->checked_for_resel = 1;
/* NOTE: We might already have system information from previous
- * scan. But we need recent informations, so we scan again!
+ * scan. But we need recent information, so we scan again!
*/
/* Tune to frequency for a while, to receive broadcasts. */
@@ -4451,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)
@@ -4523,7 +4477,7 @@ static int gsm322_nb_start(struct osmocom_ms *ms, int synced)
if (!changed && cs->nb_meas_set)
return 0;
- /* start neigbour cell measurement task */
+ /* start neighbour cell measurement task */
num = 0;
llist_for_each_entry(nb, &cs->nb_list, entry) {
if (nb->state == GSM322_NB_NOT_SUP)
@@ -4619,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;
@@ -4701,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 */
@@ -4847,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));
}
@@ -4877,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)) {
@@ -4920,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",
@@ -4970,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) {
@@ -5013,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, "- |- |- |");
@@ -5080,10 +5038,11 @@ int gsm322_init(struct osmocom_ms *ms)
s_rc = fgets(version, sizeof(version), fp);
version[sizeof(version) - 1] = '\0';
if (!s_rc || !!strcmp(ba_version, version)) {
- LOGP(DCS, LOGL_NOTICE, "BA version missmatch, "
+ LOGP(DCS, LOGL_NOTICE, "BA version mismatch, "
"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);
@@ -5094,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
@@ -5114,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");
@@ -5145,38 +5162,14 @@ int gsm322_exit(struct osmocom_ms *ms)
gsm_print_arfcn(index2arfcn(i)));
talloc_free(cs->list[i].sysinfo);
cs->list[i].sysinfo = NULL;
+ cs->si = NULL;
}
cs->list[i].flags = 0;
}
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 45decfd9..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 successfull\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
new file mode 100644
index 00000000..90fc2dfd
--- /dev/null
+++ b/src/host/layer23/src/mobile/gsm414.c
@@ -0,0 +1,218 @@
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_04_14.h>
+#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>
+
+int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause);
+int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
+ struct msgb *msg, uint8_t link_id);
+struct msgb *gsm48_l3_msgb_alloc(void);
+
+#define loop_mode_name(mode) \
+ get_value_string(loop_mode_names, mode)
+
+static const struct value_string loop_mode_names[] = {
+ { L1CTL_TCH_LOOP_OPEN, "(OPEN)" },
+ { L1CTL_TCH_LOOP_A, "A" },
+ { L1CTL_TCH_LOOP_B, "B" },
+ { L1CTL_TCH_LOOP_C, "C" },
+ { L1CTL_TCH_LOOP_D, "D" },
+ { L1CTL_TCH_LOOP_E, "E" },
+ { L1CTL_TCH_LOOP_F, "F" },
+ { L1CTL_TCH_LOOP_I, "I" },
+ { 0, NULL }
+};
+
+static struct msgb *alloc_gsm414_msg(uint8_t msg_type)
+{
+ struct gsm48_hdr *ngh;
+ struct msgb *nmsg;
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (nmsg == NULL)
+ return NULL;
+
+ ngh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*ngh));
+ ngh->proto_discr = GSM48_PDISC_TEST;
+ ngh->msg_type = msg_type;
+
+ return nmsg;
+}
+
+static int handle_close_tch_loop(struct osmocom_ms *ms, const struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ const struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int msg_len = msgb_l3len(msg);
+ struct msgb *nmsg;
+
+ /* Make sure that we have an active connection */
+ if (rr->state != GSM48_RR_ST_DEDICATED) {
+ LOGP(DMM, LOGL_NOTICE, "TCH loop requires an active connection\n");
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT);
+ return -EINVAL;
+ }
+
+ /* Make sure that the established channel is either TCH/F or TCH/H */
+ if ((rr->cd_now.chan_nr & 0xf8) != RSL_CHAN_Bm_ACCHs
+ && (rr->cd_now.chan_nr & 0xf0) != RSL_CHAN_Lm_ACCHs) {
+ LOGP(DMM, LOGL_NOTICE, "TCH loop requires a TCH/F or TCH/H connection\n");
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT);
+ return -EINVAL;
+ }
+
+ /* Check if a loop is already closed */
+ if (rr->tch_loop_mode != L1CTL_TCH_LOOP_OPEN) {
+ LOGP(DMM, LOGL_NOTICE, "TCH loop has already been closed\n");
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT);
+ return -EINVAL;
+ }
+
+ if ((msg_len - sizeof(*gh)) < 1)
+ return -EINVAL;
+
+ /* Parse type of the TCH test loop, convert to L1CTL format */
+ uint8_t gsm414_loop_mode = (gh->data[0] >> 1) & 0x1f;
+
+ /* NOTE: some bits are not specified, so they can be 0 or 1 */
+ if (gsm414_loop_mode == GSM414_LOOP_A)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_A;
+ else if (gsm414_loop_mode == GSM414_LOOP_B)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_B;
+ else if ((gsm414_loop_mode & 0x1e) == GSM414_LOOP_C)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_C;
+ else if ((gsm414_loop_mode & 0x1c) == GSM414_LOOP_D)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_D;
+ else if ((gsm414_loop_mode & 0x1c) == GSM414_LOOP_E)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_E;
+ else if ((gsm414_loop_mode & 0x1c) == GSM414_LOOP_F)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_F;
+ else if ((gsm414_loop_mode & 0x1c) == GSM414_LOOP_I)
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_I;
+ else {
+ LOGP(DMM, LOGL_NOTICE, "Unhandled 3GPP TS 44.014 TCH loop "
+ "mode=0x%02x => rejecting\n", gsm414_loop_mode);
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N);
+ return -ENOTSUP;
+ }
+
+ LOGP(DMM, LOGL_NOTICE, "(%s) Closing 3GPP TS 44.014 TCH loop mode '%s'\n",
+ rsl_chan_nr_str(rr->cd_now.chan_nr), loop_mode_name(rr->tch_loop_mode));
+
+ /* 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->cd_now.tch_flags, rr->tch_loop_mode);
+
+ /* Craft and send the ACKnowledgement */
+ nmsg = alloc_gsm414_msg(GSM414_MT_CLOSE_TCH_LOOP_ACK);
+ if (nmsg == NULL)
+ return -ENOMEM;
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
+}
+
+static int handle_open_tch_loop(struct osmocom_ms *ms, const struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+
+ /* Make sure that we have an active connection */
+ if (rr->state != GSM48_RR_ST_DEDICATED) {
+ LOGP(DMM, LOGL_NOTICE, "TCH loop requires an active connection\n");
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT);
+ return -EINVAL;
+ }
+
+ /* Make sure that the established channel is either TCH/F or TCH/H */
+ if ((rr->cd_now.chan_nr & 0xf8) != RSL_CHAN_Bm_ACCHs
+ && (rr->cd_now.chan_nr & 0xf0) != RSL_CHAN_Lm_ACCHs) {
+ LOGP(DMM, LOGL_NOTICE, "TCH loop requires a TCH/F or TCH/H connection\n");
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT);
+ return -EINVAL;
+ }
+
+ /* Check if a loop actually needs to be closed */
+ if (rr->tch_loop_mode == L1CTL_TCH_LOOP_OPEN) {
+ LOGP(DMM, LOGL_NOTICE, "TCH loop has not been closed (already open)\n");
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT);
+ return -EINVAL;
+ }
+
+ LOGP(DMM, LOGL_NOTICE, "(%s) Opening 3GPP TS 44.014 TCH loop mode '%s'\n",
+ 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, 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;
+ rr->tch_loop_mode = L1CTL_TCH_LOOP_OPEN;
+ if (!needs_ack)
+ return 0;
+
+ /* Craft and send the ACKnowledgement */
+ nmsg = alloc_gsm414_msg(GSM414_MT_OPEN_LOOP_CMD);
+ if (nmsg == NULL)
+ return -ENOMEM;
+
+ msgb_put_u8(nmsg, GSM414_OPEN_LOOP_ACK_IE);
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0);
+}
+
+int gsm414_rcv_test(struct osmocom_ms *ms, const struct msgb *msg)
+{
+ const struct gsm48_hdr *gh = msgb_l3(msg);
+
+ LOGP(DMM, LOGL_INFO, "Received 3GPP TS 44.014 message '%s' (0x%02x)\n",
+ get_value_string(gsm414_msgt_names, gh->msg_type), gh->msg_type);
+
+ /* TODO: check if the test SIM (special EF.ADM) is inserted */
+ switch (gh->msg_type) {
+ case GSM414_MT_CLOSE_TCH_LOOP_CMD:
+ return handle_close_tch_loop(ms, msg);
+ case GSM414_MT_OPEN_LOOP_CMD:
+ return handle_open_tch_loop(ms, msg);
+ default:
+ LOGP(DMM, LOGL_NOTICE, "Unhandled 3GPP TS 44.014 message '%s' (0x%02x)\n",
+ get_value_string(gsm414_msgt_names, gh->msg_type), gh->msg_type);
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N);
+ return -ENOTSUP;
+ }
+}
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 52e43b9b..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,8 +287,15 @@ 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);
+}
+
/*
- * endcoding
+ * encoding
*/
#define GSM480_ALLOC_SIZE 512+128
@@ -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;
@@ -933,7 +946,7 @@ static int gsm480_rx_result(struct gsm_trans *trans, const uint8_t *data,
LOGP(DSS, LOGL_NOTICE, "Invoke ID mismatch\n");
}
}
- /* Store invoke ID, in case we wan't to send a result. */
+ /* Store invoke ID, in case we want to send a result. */
trans->ss.invoke_id = tag_data[0];
len -= tag_data - data + tag_len;
data = tag_data + 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 f1e81098..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;
@@ -1750,7 +1809,7 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
}
- /* in case we receive a relase, when we are already in NULL state */
+ /* in case we receive a release, when we are already in NULL state */
if (trans->cc.state == GSM_CSTATE_NULL) {
LOGP(DCC, LOGL_INFO, "ignoring RELEASE in NULL state\n");
/* release MM conn, free trans */
@@ -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 872dce55..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
@@ -109,7 +114,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
* - cell selected
* - cell == SIM LAI
*
- * Otherwhise PLMN SEARCH is entered.
+ * Otherwise PLMN SEARCH is entered.
*
* During PLMN SEARCH NORMAL state: (4.2.2.5)
* - on expirery of T3212: Perform periodic location update, when back
@@ -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,16 +195,56 @@ 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
- * state. Depending on the conditions above, the appropiate state is selected.
+ * state. Depending on the conditions above, the appropriate state is selected.
*
*
* gsm48_mm_return_idle() is used to select the Service state when returning
@@ -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 */
@@ -1006,7 +1160,7 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
if (s->t3212) /* still required? */
gsm48_mm_loc_upd_periodic(ms, NULL);
else
- LOGP(DMM, LOGL_INFO, "but not requred\n");
+ LOGP(DMM, LOGL_INFO, "but not required\n");
/* must exit, because this function can be called
* recursively
*/
@@ -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;
@@ -2135,7 +2354,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
/* (re)start only if we still require location update */
if (!mm->lupd_pending) {
LOGP(DMM, LOGL_INFO, "No loc. upd. pending.\n");
- /* use MM IDLE to selecte the idle state */
+ /* use MM IDLE to select the idle state */
return gsm48_mm_return_idle(ms, NULL);
}
@@ -2159,7 +2378,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
if (!nmsg)
return -ENOMEM;
gsm322_plmn_sendmsg(ms, nmsg);
- /* use MM IDLE to selecte the idle state */
+ /* use MM IDLE to select the idle state */
return gsm48_mm_return_idle(ms, NULL);
}
@@ -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,27 +2613,29 @@ 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;
- /* RA was successfull */
+ /* RA was successful */
mm->lupd_ra_failure = 0;
/* stop periodic location updating timer */
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 */
@@ -2531,7 +2738,7 @@ static int gsm48_mm_rx_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
return -EINVAL;
}
- /* RA was successfull */
+ /* RA was successful */
mm->lupd_ra_failure = 0;
/* stop periodic location updating 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;
@@ -2674,7 +2879,7 @@ static int gsm48_mm_loc_upd_delay_retry(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
-/* process failues as described in the lower part of 4.4.4.9 */
+/* process failures as described in the lower part of 4.4.4.9 */
static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
@@ -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;
@@ -2755,7 +2958,7 @@ static int gsm48_mm_rel_loc_upd_abort(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
- /* RA was successfull or sent twice */
+ /* RA was successful or sent twice */
mm->lupd_ra_failure = 0;
/* continue with failure handling */
@@ -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;
@@ -2938,7 +3139,7 @@ static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
/* release MM connection(s) */
gsm48_mm_release_mm_conn(ms, abort_any, 16, 0, 0);
- /* state depends on the existance of remaining MM connections */
+ /* state depends on the existence of remaining MM connections */
if (llist_empty(&mm->mm_conn))
new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
else
@@ -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);
@@ -3395,7 +3597,7 @@ static int gsm48_mm_timeout_mm_con(struct osmocom_ms *ms, struct msgb *msg)
/* release pending connection */
gsm48_mm_release_mm_conn(ms, 0, 102, 0, 0);
- /* state depends on the existance of remaining MM connections */
+ /* state depends on the existence of remaining MM connections */
if (llist_empty(&mm->mm_conn)) {
/* start RR release timer */
start_mm_t3240(mm);
@@ -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,11 +3663,11 @@ 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);
- /* state depends on the existance of remaining MM connections */
+ /* state depends on the existence of remaining MM connections */
if (llist_empty(&mm->mm_conn)) {
/* start RR release timer */
start_mm_t3240(mm);
@@ -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;
@@ -3800,7 +4486,7 @@ int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg)
return rc;
}
-/* state trasitions for radio ressource messages (lower layer) */
+/* state trasitions for radio resource messages (lower layer) */
static struct rrdatastate {
uint32_t states;
int type;
@@ -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:
@@ -4059,31 +4771,45 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
msgb_free(msg);
return 0;
}
- break; /* follow the selection proceedure below */
+ 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;
- case 0x0f: /* test TS 04.14 */
- LOGP(DMM, LOGL_NOTICE, "Test protocol 0x%02x according to "
- "TS 04.14 is not supported.\n", pdisc);
- goto status;
default:
LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n",
- pdisc);
-status:
+ pdisc);
msgb_free(msg);
return gsm48_mm_tx_mm_status(ms,
GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
@@ -4165,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),
@@ -4200,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),
@@ -4235,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},
@@ -4295,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 \
@@ -4323,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 a94fc99c..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,9 +34,56 @@
* 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) assigment / handover
+/* Testing delayed (immediate) assignment / handover
*
* When enabled, the starting time will be set by given frames in the future.
* If a starting time is given by the network, this time is ignored.
@@ -73,32 +116,49 @@
#include <osmocom/core/bitvec.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)
@@ -210,7 +270,7 @@ static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg)
case GSM48_PDISC_MM:
case GSM48_PDISC_CC:
case GSM48_PDISC_NC_SS:
- /* all thre pdiscs share the same V(SD) */
+ /* all three pdiscs share the same V(SD) */
pdisc = GSM48_PDISC_MM;
// fall through
case GSM48_PDISC_GROUP_CC:
@@ -249,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;
@@ -307,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;
@@ -319,7 +435,7 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
int gsm48_rr_alter_delay(struct osmocom_ms *ms)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
- struct gsm_settings *set = &rr->ms->settings;
+ struct gsm_settings *set = &ms->settings;
if (rr->state != GSM48_RR_ST_DEDICATED)
return -EINVAL;
@@ -343,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) {
@@ -369,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;
@@ -411,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;
}
}
@@ -451,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 }
};
@@ -515,8 +681,8 @@ int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg)
}
/* push rsl header and send (RSL-SAP) */
-static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
- struct msgb *msg, uint8_t link_id)
+int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
+ struct msgb *msg, uint8_t link_id)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
@@ -632,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];
- /* don't monitor if no cell is selcted or if we scan neighbour cells */
+ 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), berr, snr,
- 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);
@@ -765,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;
@@ -817,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)) {
@@ -873,12 +1100,36 @@ 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
*/
/* send rr status request */
-static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
+int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
{
struct msgb *nmsg;
struct gsm48_hdr *gh;
@@ -893,7 +1144,7 @@ static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
st = (struct gsm48_rr_status *) msgb_put(nmsg, sizeof(*st));
gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL;
+ gh->msg_type = GSM48_MT_RR_STATUS;
/* rr cause */
st->rr_cause = cause;
@@ -908,11 +1159,9 @@ static 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);
@@ -925,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);
@@ -1278,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
*/
@@ -1302,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 */
@@ -1454,7 +2340,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging,
if (s->neci) {
chan_req_mask = 0x0f;
chan_req_val = 0x10;
- LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OHTER "
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER "
"with NECI)\n", chan_req_val);
} else {
chan_req_mask = 0x1f;
@@ -1483,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 */
@@ -1518,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)
@@ -1536,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;
@@ -1593,11 +2477,11 @@ fail:
rr->n_chan_req--;
if (rr->wait_assign == 0) {
- /* first random acces, without delay of slots */
+ /* first random access, without delay of slots */
slots = 0;
rr->wait_assign = 1;
} else {
- /* subsequent random acces, with slots from table 3.1 */
+ /* subsequent random access, with slots from table 3.1 */
switch(s->tx_integer) {
case 3: case 8: case 14: case 50:
if (s->ccch_conf != 1) /* not combined CCCH */
@@ -1707,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 */
@@ -1777,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));
@@ -1806,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));
@@ -1835,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));
@@ -1864,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));
@@ -1894,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));
@@ -1929,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);
}
@@ -1944,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 "
@@ -1960,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));
@@ -1973,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"
@@ -1989,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;
@@ -2003,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"
@@ -2019,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;
@@ -2033,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 "
@@ -2050,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);
@@ -2065,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
*/
@@ -2077,46 +3027,38 @@ static int gsm48_rr_chan2cause[4] = {
RR_EST_CAUSE_ANS_PAG_TCH_ANY
};
-/* given LV of mobile identity is checked agains ms */
-static uint8_t gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
+/* given LV of mobile identity is checked against ms */
+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;
@@ -2215,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);
@@ -2226,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);
@@ -2285,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);
@@ -2296,20 +3232,16 @@ 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);
} else
LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n",
ntohl(pa->tmsi2));
- /* thrid MI */
+ /* 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);
@@ -2318,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);
@@ -2335,24 +3265,23 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
* (immediate) assignment
*/
-/* match request reference agains request history */
-static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref)
+/* match request reference against request history */
+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 "
@@ -2387,7 +3316,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
/* ignore imm.ass. while not camping on a cell */
if (!cs->selected || cs->neighbour || !s) {
- LOGP(DRR, LOGL_INFO, "IMMEDIATED ASSGINMENT ignored, we are "
+ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are "
"have not proper selected the serving cell.\n");
return 0;
@@ -2423,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,
@@ -2437,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,
@@ -2459,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 */
@@ -2495,7 +3428,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
/* ignore imm.ass.ext while not camping on a cell */
if (!cs->selected || cs->neighbour || !s) {
- LOGP(DRR, LOGL_INFO, "IMMEDIATED ASSGINMENT ignored, we are "
+ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT ignored, we are "
"have not proper selected the serving cell.\n");
return 0;
@@ -2537,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,
@@ -2551,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,
@@ -2561,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,
@@ -2575,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,
@@ -2597,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 */
@@ -2613,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 */
@@ -2663,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)
@@ -2671,7 +3612,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
/* start timer 3126 if not already */
if (!osmo_timer_pending(&rr->t3126))
start_rr_t3126(rr, 5, 0); /* TODO improve! */
- /* stop assignmnet requests */
+ /* stop assignment requests */
rr->n_chan_req = 0;
/* wait until timer 3126 expires, then release
@@ -2683,7 +3624,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
return 0;
}
-/* 9.1.1 ADDITIONAL ASSIGMENT is received */
+/* 9.1.1 ADDITIONAL ASSIGNMENT is received */
static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -2692,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);
}
@@ -2732,7 +3674,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
if ((s->si5bis && s->nb_ext_ind_si5
&& s->nb_ba_ind_si5bis != rep_ba)
|| (s->si5ter && s->nb_ba_ind_si5ter != rep_ba)) {
- LOGP(DRR, LOGL_NOTICE, "BA-IND missmatch on SI5*");
+ LOGP(DRR, LOGL_NOTICE, "BA-IND mismatch on SI5*");
} else
rep_valid = 1;
}
@@ -2751,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 */
@@ -2765,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 */
@@ -2876,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);
@@ -2980,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 */
@@ -3021,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;
}
@@ -3033,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;
@@ -3054,7 +4010,7 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd,
LOGP(DRR, LOGL_INFO, "using cell channel descr.\n");
if (cd->cell_desc_lv[0] != 16) {
LOGP(DRR, LOGL_ERROR, "cell channel descr. "
- "has invalid lenght\n");
+ "has invalid length\n");
return GSM48_RR_CAUSE_ABNORMAL_UNSPEC;
}
gsm48_decode_freq_list(freq, cd->cell_desc_lv + 1, 16,
@@ -3137,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)))) {
@@ -3154,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;
@@ -3233,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
@@ -3326,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:
@@ -3372,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);
@@ -3395,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);
@@ -3412,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, chanel mode modify, assignment, and handover
+ * 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);
+ /* 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;
}
@@ -3469,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,
@@ -3479,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);
}
@@ -3559,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");
@@ -3573,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,
@@ -3584,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 */
@@ -3684,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,
@@ -3707,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),
@@ -3719,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,
@@ -3730,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);
@@ -4057,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 */
@@ -4070,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 *)
@@ -4085,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,
@@ -4096,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),
@@ -4108,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,
@@ -4119,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);
@@ -4366,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)
{
@@ -4388,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;
}
@@ -4488,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) {
@@ -4537,7 +5599,7 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
goto reject;
}
- /* check for relevant informations */
+ /* check for relevant information */
if (!s->si3) {
LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n");
cause = RR_REL_CAUSE_TRY_LATER;
@@ -4575,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)
{
@@ -4623,17 +5992,21 @@ 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;
+ int rc = -EINVAL;
if (pdisc == GSM48_PDISC_RR) {
- int rc = -EINVAL;
uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4;
/* ignore if skip indicator is not B'0000' */
- if (skip_ind)
+ if (skip_ind) {
+ msgb_free(msg);
return 0;
+ }
switch(gh->msg_type) {
case GSM48_MT_RR_ADD_ASS:
@@ -4660,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;
@@ -4673,6 +6049,10 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
msgb_free(msg);
return rc;
+ } else if (pdisc == GSM48_PDISC_TEST) {
+ rc = gsm414_rcv_test(ms, msg);
+ msgb_free(msg);
+ return rc;
}
/* pull off RSL header up to L3 message */
@@ -4682,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);
}
@@ -4704,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",
@@ -4718,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);
@@ -4732,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",
@@ -4741,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;
}
}
@@ -4790,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;
@@ -4814,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);
@@ -4832,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);
@@ -4842,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",
@@ -4900,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:
@@ -4946,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;
@@ -4994,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)
@@ -5156,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 "
@@ -5250,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},
};
@@ -5351,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");
@@ -5364,7 +6844,7 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg)
}
-/* input function for L2 messags up to L3 */
+/* input function for L2 messages up to L3 */
static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg)
{
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
@@ -5407,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 \
@@ -5501,7 +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;
}
@@ -5531,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;
}
@@ -5574,75 +7078,34 @@ 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 acces 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
-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;
if (!rr->dm_est) {
LOGP(DRR, LOGL_INFO, "Current channel is not active\n");
- msgb_free(msg);
- return -ENOTSUP;
+ 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");
- msgb_free(msg);
- return -ENOTSUP;
+ 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;
+ }
+
+ 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:
+ msgb_free(msg);
+ return -ENOTSUP;
}
int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode)
@@ -5657,11 +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);
+ 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 9764b333..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 "
@@ -166,7 +164,7 @@ void sighandler(int sigset)
/* The first signal causes initiating of shutdown with detach
* procedure. The second signal causes initiating of shutdown
* without detach procedure. The third signal will exit process
- * immidiately. (in case it hangs)
+ * immediately. (in case it hangs)
*/
if (count_int == 0) {
fprintf(stderr, "Performing shutdown with clean "
@@ -188,9 +186,16 @@ void sighandler(int sigset)
osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_SHUTDOWN, &_quit);
break;
case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
*/
+ talloc_report_full(l23_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
case SIGUSR1:
case SIGUSR2:
talloc_report_full(l23_ctx, stderr);
@@ -198,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));
@@ -217,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) {
@@ -250,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);
@@ -274,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 d7d56cc0..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,
@@ -72,13 +74,13 @@ int mncc_sock_from_cc(struct mncc_sock_state *state, struct msgb *msg)
/* Actually enqueue the message and mark socket write need */
msgb_enqueue(&state->upqueue, msg);
- state->conn_bfd.when |= BSC_FD_WRITE;
+ osmo_fd_write_enable(&state->conn_bfd);
return 0;
}
void mncc_sock_write_pending(struct mncc_sock_state *state)
{
- state->conn_bfd.when |= BSC_FD_WRITE;
+ osmo_fd_write_enable(&state->conn_bfd);
}
static void mncc_sock_close(struct mncc_sock_state *state)
@@ -87,16 +89,16 @@ 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 */
- state->listen_bfd.when |= BSC_FD_READ;
+ osmo_fd_read_enable(&state->listen_bfd);
/* FIXME: make sure we don't enqueue anymore */
- /* release all exisitng calls */
+ /* release all existing calls */
mncc_clear_trans(state->inst, GSM48_PDISC_CC);
/* flush the queue */
@@ -156,7 +158,7 @@ static int mncc_sock_write(struct osmo_fd *bfd)
msg = llist_entry(state->upqueue.next, struct msgb, list);
mncc_prim = (struct gsm_mncc *)msg->data;
- bfd->when &= ~BSC_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -171,7 +173,7 @@ static int mncc_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= BSC_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -195,12 +197,12 @@ static int mncc_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
- if (flags & BSC_FD_READ)
+ if (flags & OSMO_FD_READ)
rc = mncc_sock_read(bfd);
if (rc < 0)
return rc;
- if (flags & BSC_FD_WRITE)
+ if (flags & OSMO_FD_WRITE)
rc = mncc_sock_write(bfd);
return rc;
@@ -226,16 +228,12 @@ static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have "
"another active connection ?!?\n");
/* We already have one MNCC app connected, this is all we support */
- state->listen_bfd.when &= ~BSC_FD_READ;
+ osmo_fd_read_disable(&state->listen_bfd);
close(rc);
return 0;
}
- conn_bfd->fd = rc;
- conn_bfd->when = BSC_FD_READ;
- conn_bfd->cb = mncc_sock_cb;
- conn_bfd->data = state;
-
+ osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, mncc_sock_cb, state, 0);
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n");
close(conn_bfd->fd);
diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c
index 22432913..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,114 +89,452 @@ 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;
/* if no specific speech_ver is given */
if (speech_ver < 0) {
- /* if half rate is supported and prefered */
+ /* 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 prefered */
+ /* 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 imcomplete\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 2979e7cd..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>
@@ -497,12 +494,13 @@ static int lua_fd_cb(struct osmo_fd *fd, unsigned int what) {
static int lua_register_fd(lua_State *L)
{
struct fd_userdata *fdu;
+ int fd;
/* fd, cb */
luaL_argcheck(L, lua_isnumber(L, -2), 1, "needs to be a filedescriptor");
luaL_argcheck(L, lua_isfunction(L, -1), 2, "Callback needs to be a function");
- /* Cretae a table so a user can unregister (and unregister on GC) */
+ /* Create a table so a user can unregister (and unregister on GC) */
fdu = lua_newuserdata(L, sizeof(*fdu));
fdu->state = L;
fdu->fd.fd = -1;
@@ -510,10 +508,8 @@ static int lua_register_fd(lua_State *L)
lua_setmetatable(L, -2);
/* Set the filedescriptor */
- fdu->fd.fd = (int) lua_tonumber(L, -3);
- fdu->fd.cb = lua_fd_cb;
- fdu->fd.when = BSC_FD_READ;
- fdu->fd.data = fdu;
+ fd = (int) lua_tonumber(L, -3);
+ osmo_fd_setup(&fdu->fd, fd, OSMO_FD_READ, lua_fd_cb, fdu, 0);
/* Assuming that an error here will lead to a GC */
if (osmo_fd_register(&fdu->fd) != 0) {
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 b7678337..00000000
--- a/src/host/layer23/src/mobile/voice.c
+++ /dev/null
@@ -1,78 +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/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;
-
- /* distribute and then free */
- if (ms->mncc_entity.mncc_recv && ms->mncc_entity.ref) {
- /* push mncc header in front of data */
- mncc = (struct gsm_data_frame *)
- msgb_push(msg, sizeof(struct gsm_data_frame));
- mncc->msg_type = GSM_TCHF_FRAME;
- mncc->callref = ms->mncc_entity.ref;
- ms->mncc_entity.mncc_recv(ms, mncc->msg_type, mncc);
- }
-
- msgb_free(msg);
- return 0;
-}
-
-/*
- * send voice
- */
-int gsm_send_voice(struct osmocom_ms *ms, struct gsm_data_frame *data)
-{
- struct msgb *nmsg;
-
- nmsg = msgb_alloc_headroom(33 + 64, 64, "TCH/F");
- if (!nmsg)
- return -ENOMEM;
- nmsg->l2h = msgb_put(nmsg, 33);
- memcpy(nmsg->l2h, data->data, 33);
-
- 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 2001b882..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,49 +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
};
-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);
+struct cmd_node vgcs_node = {
+ VGCS_NODE,
+ "%s(group-call)# ",
+ 1
+};
- 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)
{
@@ -110,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;
@@ -130,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;
@@ -204,26 +196,24 @@ 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 ressource layer state: %s%s",
+ vty_out(vty, " radio resource layer state: %s%s",
gsm48_rr_state_names[ms->rrlayer.state], VTY_NEWLINE);
vty_out(vty, " mobility management layer state: %s",
gsm48_mm_state_names[ms->mmlayer.state]);
@@ -239,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;
@@ -251,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;
@@ -313,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;
@@ -332,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;
@@ -352,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;
}
@@ -363,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;
}
@@ -378,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;
}
@@ -408,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;
}
@@ -423,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, 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;
- gsm322_dump_forbidden_la(ms, print_vty, vty);
+ 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;
}
@@ -437,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;
@@ -452,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;
@@ -461,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 bulit 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);
-}
-
-DEFUN(sim_test_att, sim_test_att_cmd,
- "sim testcard MS_NAME MCC MNC LAC TMSI attached",
- "SIM actions\nAttach bulit 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);
-}
+#define CALL_CMD "call MS_NAME"
+#define CALL_CMD_DESC \
+ "Call related commands\n" \
+ "Name of MS (see \"show ms\")\n"
-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);
+}
- gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99);
+#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;
+
+ 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);
+}
+
+#define CALL_PARAMS_ASYNC_CMD "async"
+#define CALL_PARAMS_ASYNC_CMD_DESC \
+ "Asynchronous connection params (does not apply to FAX calls)\n"
+
+#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"
- ms->subscr.mcc = mcc;
- ms->subscr.mnc = mnc;
- ms->subscr.lac = lac;
- ms->subscr.tmsi = 0xffffffff;
+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;
- gsm_subscr_write_loci(ms);
+ 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 incomming 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",
@@ -907,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;
@@ -966,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;
@@ -975,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;
@@ -1008,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;
}
@@ -1038,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;
@@ -1052,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;
}
@@ -1065,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;
@@ -1171,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\")")
@@ -1202,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);
@@ -1305,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);
@@ -1316,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);
+ 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_L1PHY:
- vty_out(vty, " sim reader%s", VTY_NEWLINE);
- break;
- case GSM_SIM_TYPE_TEST:
- vty_out(vty, " sim test%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)
@@ -1377,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) {
@@ -1413,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)
@@ -1433,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",
@@ -1479,59 +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 OSMO_AUTH_ALG_COMP128v1:
- vty_out(vty, " ki comp128 %s%s",
- osmo_hexdump(set->test_ki, 16), VTY_NEWLINE);
+ case GSM48_BCAP_TR_RLP_PREF:
+ vty_out(vty, " call-params ce non-transparent prefer%s", VTY_NEWLINE);
+ break;
+ 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);
-
- /* 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)
@@ -1549,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);
@@ -1569,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 bulit 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;
}
@@ -1652,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]);
@@ -2199,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;
@@ -2213,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; \
} \
@@ -2231,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; \
} \
@@ -2249,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; \
@@ -2260,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; \
@@ -2271,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"
@@ -2332,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);
@@ -2353,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);
@@ -2374,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);
@@ -2394,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);
@@ -2414,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);
@@ -2440,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;
@@ -2455,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"
@@ -2526,251 +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);
+/* TCH config */
+DEFUN(cfg_ms_tch_voice,
+ cfg_ms_tch_voice_cmd,
+ "tch-voice", "Configure TCH (Traffic CHannel) params for voice calls\n")
+{
+ vty->node = TCH_VOICE_NODE;
return CMD_SUCCESS;
}
-DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI",
- "Set IMSI on test card\n15 digits IMSI")
+ALIAS_DEPRECATED(cfg_ms_tch_voice, /* alias to 'tch-voice' */
+ cfg_ms_audio_cmd,
+ "audio", "(deprecated alias for 'tch-voice')\n");
+
+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;
- char *error = gsm_check_imsi(argv[0]);
- if (error) {
- vty_out(vty, "%s%s", error, VTY_NEWLINE);
- return CMD_WARNING;
+ 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;
+ }
}
- strcpy(set->test_imsi, argv[0]);
+#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
- vty_restart_if_started(vty, ms);
+ set->tch_voice.io_handler = (enum tch_voice_io_handler)val;
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)
+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;
- 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->tch_voice.io_handler = TCH_VOICE_IOH_NONE;
- 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_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;
- 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);
+ OSMO_ASSERT(val >= 0);
+
+ 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->test_ki_type = OSMO_AUTH_ALG_COMP128v1;
- memcpy(set->test_ki, ki, 16);
+ set->tch_voice.io_format = val;
+
return CMD_SUCCESS;
}
-DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access",
- "Allow access to barred cells")
+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;
- set->test_barr = 1;
+ OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_output_dev, argv[0]);
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_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;
- set->test_barr = 0;
+ OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_input_dev, argv[0]);
return CMD_SUCCESS;
}
-DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn",
- NO_STR "Unset Registered PLMN")
+DEFUN(cfg_ms_tch_data,
+ cfg_ms_tch_data_cmd,
+ "tch-data", "Configure TCH (Traffic CHannel) params for data calls\n")
{
- struct osmocom_ms *ms = vty->index;
- struct gsm_settings *set = &ms->settings;
-
- set->test_rplmn_valid = 0;
-
- vty_restart_if_started(vty, ms);
-
+ vty->node = TCH_DATA_NODE;
return CMD_SUCCESS;
}
-static int _test_rplmn_cmd(struct vty *vty, int argc, const char *argv[],
- int attached)
+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 = 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;
- 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_ASSERT(val >= 0);
+ set->tch_data.io_handler = (enum tch_data_io_handler)val;
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_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")
{
- struct osmocom_ms *ms = vty->index;
+ struct osmocom_ms *ms = (struct osmocom_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);
+ 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;
}
@@ -2798,24 +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:
- vty->node = MS_NODE;
- break;
- default:
- vty->node = CONFIG_NODE;
- }
-
- return vty->node;
-}
-
DEFUN(off, off_cmd, "off",
"Turn mobiles off (shutdown) and exit")
{
@@ -2824,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);
@@ -2872,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);
@@ -2917,122 +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);
- /* 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 f4d91113..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>
@@ -500,7 +496,7 @@ static int romload_prepare_block(void)
dnload.block_ptr = dnload.block;
dnload.block_number++;
- dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ | OSMO_FD_WRITE);
return 0;
}
@@ -586,7 +582,7 @@ static int handle_write_block(void)
printf("Progress: %i%%\r", progress);
fflush(stdout);
dnload.write_ptr = dnload.data;
- dnload.serial_fd.when &= ~BSC_FD_WRITE;
+ osmo_fd_write_disable(&dnload.serial_fd);
if (dnload.romload_state == SENDING_LAST_BLOCK) {
dnload.romload_state = LAST_BLOCK_SENT;
printf("Finished, sent %i blocks in total\n",
@@ -638,7 +634,7 @@ static int handle_write_dnload(void)
} else if (dnload.write_ptr >= dnload.data + dnload.data_len) {
printf("finished\n");
dnload.write_ptr = dnload.data;
- dnload.serial_fd.when &= ~BSC_FD_WRITE;
+ osmo_fd_write_disable(&dnload.serial_fd);
return 1;
}
@@ -682,7 +678,7 @@ static int handle_sercomm_write(void)
}
if (end)
- dnload.serial_fd.when &= ~BSC_FD_WRITE;
+ osmo_fd_write_disable(&dnload.serial_fd);
return 0;
}
@@ -751,7 +747,7 @@ static void hdlc_send_to_phone(uint8_t dlci, uint8_t *data, int len)
sercomm_sendmsg(dlci, msg);
- dnload.serial_fd.when |= BSC_FD_WRITE;
+ osmo_fd_write_enable(&dnload.serial_fd);
}
static void hdlc_console_cb(uint8_t dlci, struct msgb *msg)
@@ -842,12 +838,12 @@ static int handle_read(void)
}
} else if (!memcmp(buffer, phone_prompt2, sizeof(phone_prompt2))) {
printf("Received PROMPT2 from phone, starting download\n");
- dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ | OSMO_FD_WRITE);
dnload.state = DOWNLOADING;
} else if (!memcmp(buffer, phone_ack, sizeof(phone_ack))) {
printf("Received DOWNLOAD ACK from phone, your code is"
" running now!\n");
- dnload.serial_fd.when = BSC_FD_READ;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ);
dnload.state = WAITING_PROMPT1;
dnload.write_ptr = dnload.data;
dnload.expect_hdlc = 1;
@@ -868,18 +864,18 @@ static int handle_read(void)
} else if (!memcmp(buffer, phone_nack, sizeof(phone_nack))) {
printf("Received DOWNLOAD NACK from phone, something went"
" wrong :(\n");
- dnload.serial_fd.when = BSC_FD_READ;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ);
dnload.state = WAITING_PROMPT1;
dnload.write_ptr = dnload.data;
} else if (!memcmp(buffer, phone_nack_magic, sizeof(phone_nack_magic))) {
printf("Received MAGIC NACK from phone, you need to"
" have \"1003\" at 0x803ce0\n");
- dnload.serial_fd.when = BSC_FD_READ;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ);
dnload.state = WAITING_PROMPT1;
dnload.write_ptr = dnload.data;
} else if (!memcmp(buffer, ftmtool, sizeof(ftmtool))) {
printf("Received FTMTOOL from phone, ramloader has aborted\n");
- dnload.serial_fd.when = BSC_FD_READ;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ);
dnload.state = WAITING_PROMPT1;
dnload.write_ptr = dnload.data;
}
@@ -1000,7 +996,7 @@ static int handle_read_romload(void)
if (!memcmp(buffer, romload_branch_ack,
sizeof(romload_branch_ack))) {
printf("Received branch ack, your code is running now!\n");
- dnload.serial_fd.when = BSC_FD_READ;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ);
dnload.romload_state = FINISHED;
dnload.write_ptr = dnload.data;
dnload.expect_hdlc = 1;
@@ -1116,7 +1112,7 @@ static int handle_read_mtk(void)
printf("Received size ack\n");
dnload.expect_hdlc = 1;
dnload.mtk_state = MTK_SENDING_BLOCKS;
- dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ | OSMO_FD_WRITE);
bufptr -= 3;
break;
case MTK_SENDING_BLOCKS:
@@ -1138,7 +1134,7 @@ static int handle_read_mtk(void)
printf("Received Block %i preparing next block\n",
dnload.block_number);
mtk_prepare_block();
- dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ | OSMO_FD_WRITE);
}
break;
case MTK_WAIT_BRANCH_CMD_ACK:
@@ -1156,7 +1152,7 @@ static int handle_read_mtk(void)
break;
printf("Received branch address ack, code should run now\n");
osmo_serial_set_baudrate(dnload.serial_fd.fd, MODEM_BAUDRATE);
- dnload.serial_fd.when = BSC_FD_READ;
+ osmo_fd_update_when(&dnload.serial_fd, 0, OSMO_FD_READ);
dnload.mtk_state = MTK_FINISHED;
dnload.write_ptr = dnload.data;
dnload.expect_hdlc = 1;
@@ -1172,7 +1168,7 @@ static int handle_read_mtk(void)
static int serial_read(struct osmo_fd *fd, unsigned int flags)
{
int rc;
- if (flags & BSC_FD_READ) {
+ if (flags & OSMO_FD_READ) {
switch (dnload.mode) {
case MODE_ROMLOAD:
while ((rc = handle_read_romload()) > 0);
@@ -1188,7 +1184,7 @@ static int serial_read(struct osmo_fd *fd, unsigned int flags)
exit(2);
}
- if (flags & BSC_FD_WRITE) {
+ if (flags & OSMO_FD_WRITE) {
rc = handle_write();
if (rc == 1)
dnload.state = WAITING_PROMPT1;
@@ -1322,10 +1318,7 @@ static int tool_accept(struct osmo_fd *fd, unsigned int flags)
con->server = srv;
- con->fd.fd = rc;
- con->fd.when = BSC_FD_READ;
- con->fd.cb = un_tool_read;
- con->fd.data = con;
+ osmo_fd_setup(&con->fd, rc, OSMO_FD_READ, un_tool_read, con, 0);
if (osmo_fd_register(&con->fd) != 0) {
fprintf(stderr, "Failed to register the fd.\n");
talloc_free(con);
@@ -1389,7 +1382,7 @@ void parse_debug(const char *str)
int main(int argc, char **argv)
{
- int opt, flags;
+ int opt, flags, fd;
uint32_t tmp_load_address = ROMLOAD_ADDRESS;
const char *serial_dev = "/dev/ttyUSB1";
const char *layer2_un_path = "/tmp/osmocom_l2";
@@ -1442,12 +1435,13 @@ int main(int argc, char **argv)
dnload.filename = argv[optind];
}
- dnload.serial_fd.fd = osmo_serial_init(serial_dev, MODEM_BAUDRATE);
- if (dnload.serial_fd.fd < 0) {
+ fd = osmo_serial_init(serial_dev, MODEM_BAUDRATE);
+ if (fd < 0) {
fprintf(stderr, "Cannot open serial device %s\n", serial_dev);
exit(1);
}
+ osmo_fd_setup(&dnload.serial_fd, fd, OSMO_FD_READ, serial_read, NULL, 0);
if (osmo_fd_register(&dnload.serial_fd) != 0) {
fprintf(stderr, "Failed to register the serial.\n");
exit(1);
@@ -1458,9 +1452,6 @@ int main(int argc, char **argv)
flags |= O_NONBLOCK;
fcntl(dnload.serial_fd.fd, F_SETFL, flags);
- dnload.serial_fd.when = BSC_FD_READ;
- dnload.serial_fd.cb = serial_read;
-
/* initialize the HDLC layer */
sercomm_init();
sercomm_register_rx_cb(SC_DLCI_CONSOLE, hdlc_console_cb);
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 c9dac903..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>
@@ -57,6 +53,60 @@ static const char *tpu_addr_name[0x1f] = {
static uint8_t tpu_reg_cache[0x1f];
static uint16_t tpu_qbit;
+static uint16_t tspact_cache;
+
+static const char *tspact_name(unsigned int n)
+{
+ static char buf[32];
+
+ /* FIXME: This is rffe_dualband.c specific */
+ switch (n) {
+ case 0:
+ return "RITA_RESET";
+ case 1:
+ return "PA_ENABLE";
+ case 6:
+ return "TRENA";
+ case 8:
+ return "GSM_TXEN";
+ default:
+ snprintf(buf, sizeof(buf), "TSPACT%u", n);
+ return buf;
+ }
+}
+
+static const char *tps_strobe_name(unsigned int n)
+{
+ static char buf[32];
+
+ switch (n) {
+ case 0:
+ return "TWL3025";
+ case 2:
+ return "TRF6151";
+ default:
+ snprintf(buf, sizeof(buf), "STROBE%u", n);
+ return buf;
+ }
+}
+
+static void handle_tspact(uint16_t mask, uint16_t bits)
+{
+ uint16_t newval = (tspact_cache & mask) | bits;
+ unsigned int i;
+
+ if (newval == tspact_cache)
+ return;
+
+ for (i = 0; i < 16; i++) {
+ uint16_t shifted = (1 << i);
+ if ((tspact_cache & shifted) != (newval & shifted)) {
+ printf("%s:%d->%d ", tspact_name(i),
+ tspact_cache & shifted ? 1 : 0, newval & shifted ? 1 : 0);
+ }
+ }
+ tspact_cache = newval;
+}
static void tpu_show_instr(uint16_t tpu)
{
@@ -93,7 +143,7 @@ static void tpu_show_instr(uint16_t tpu)
switch (addr) {
case 0:
bitlen = (data & 0x1f) + 1;
- printf("DEV_IDX=%u, BITLEN=%u ", data >> 5, bitlen);
+ printf("DEV_IDX=%s, BITLEN=%u ", tps_strobe_name(data >> 5), bitlen);
if (bitlen <= 8) {
tsp_data = tpu_reg_cache[4];
printf(" TSP_DATA=0x%02x ", tsp_data);
@@ -120,6 +170,12 @@ static void tpu_show_instr(uint16_t tpu)
if (data & 0x02)
printf("WRITE ");
break;
+ case 6:
+ handle_tspact(0xff00, data);
+ break;
+ case 7:
+ handle_tspact(0x00ff, data << 8);
+ break;
}
}
printf("\n");
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 e722624c..00000000
--- a/src/host/trxcon/l1ctl.c
+++ /dev/null
@@ -1,905 +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 * FRAME_DURATION_uS / 1000);
- osmo_timer_schedule(&l1l->fbsb_timer, 0,
- timeout * FRAME_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)
-{
- int 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;
- }
-
- /* Forward hopping parameters to TRX */
- rc = trx_if_cmd_setfh(trx, h->hsn, h->maio, h->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? */
-
- msgb_free(msg);
- return 0;
-}
-
-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 b7ea262a..00000000
--- a/src/host/trxcon/l1ctl_link.c
+++ /dev/null
@@ -1,319 +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;
- conn_bfd->when = BSC_FD_READ;
- conn_bfd->data = l1l;
- conn_bfd->fd = cfd;
-
- 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)
-{
- uint16_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 = (uint16_t *) msgb_push(msg, L1CTL_MSG_LEN_FIELD);
- *len = htons(msg->len - L1CTL_MSG_LEN_FIELD);
-
- 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;
- 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;
-
- /* Bind connection handler */
- bfd->cb = l1ctl_link_accept;
- bfd->when = BSC_FD_READ;
- bfd->data = l1l;
-
- /**
- * 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 66477b24..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 = FRAME_DURATION_uS * 1000 };
-
- /* 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 > FRAME_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 > FRAME_DURATION_uS / 2) {
- timespecadd(tv_clock, &frame_duration, tv_clock);
- elapsed_us -= FRAME_DURATION_uS;
-
- sched->fn_counter_proc = 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,
- FRAME_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, FRAME_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 = TDMA_FN_SUB(fn, sched->fn_counter_proc);
-
- if (elapsed_fn >= 135774)
- elapsed_fn -= GSM_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 * FRAME_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) * FRAME_DURATION_uS * 1000;
- 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,
- FRAME_DURATION_uS * (1 - elapsed_fn));
-
- return 0;
- }
-
- /* Transmit what we still need to transmit */
- while (fn != sched->fn_counter_proc) {
- sched->fn_counter_proc = 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, FRAME_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 615d81c9..00000000
--- a/src/host/trxcon/sched_lchan_common.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: common routines for lchan handlers
- *
- * (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 <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 "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.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,
- },
-};
-
-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_lchan_desc *lchan_desc;
- struct l1ctl_info_dl dl_hdr;
- int dbm_avg;
-
- /* 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.frame_nr = htonl(lchan->rx_first_fn);
- dl_hdr.num_biterr = bit_error_count;
-
- /* Convert average RSSI to RX level */
- if (lchan->meas.num) {
- /* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */
- dbm_avg = lchan->meas.rssi_sum / lchan->meas.num;
- dl_hdr.rx_level = dbm2rxlev(dbm_avg);
- } else {
- /* No measurements, assuming the worst */
- dl_hdr.rx_level = 0;
- }
-
- /* 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);
-
- 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);
-
- 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 733e5741..00000000
--- a/src/host/trxcon/sched_lchan_pdtch.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2018 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 <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,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- 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;
- uint32_t *first_fn;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- first_fn = &lchan->rx_first_fn;
- 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);
-
- /* Reset internal state */
- if (bid == 0) {
- /* Clean up old measurements */
- memset(&lchan->meas, 0x00, sizeof(lchan->meas));
-
- *first_fn = fn;
- *mask = 0x0;
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Update measurements */
- lchan->meas.toa256_sum += toa256;
- lchan->meas.rssi_sum += rssi;
- lchan->meas.num++;
-
- /* 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;
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
-
- return -1;
- }
-
- /* 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", *first_fn,
- (*first_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, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- 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 (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 + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- return rc;
- }
-
- /* If we have sent the last (4/4) burst */
- if ((*mask & 0x0f) == 0x0f) {
- /* Confirm data / traffic sending */
- sched_send_dt_conf(trx, ts, lchan, 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 5d1f3ab9..00000000
--- a/src/host/trxcon/sched_lchan_rach.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (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 <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, uint32_t fn, uint8_t bid)
-{
- struct l1ctl_ext_rach_req *ext_req = NULL;
- struct l1ctl_rach_req *req = NULL;
- enum rach_synch_seq_t synch_seq;
- uint8_t burst[GSM_BURST_LEN];
- uint8_t *burst_ptr = 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, burst + GSM_BURST_LEN - burst_ptr);
-
- LOGP(DSCHD, LOGL_NOTICE, "Transmitting %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), fn,
- ts->index, trx_lchan_desc[lchan->type].name);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return rc;
- }
-
- /* Confirm RACH request */
- l1ctl_tx_rach_conf(trx->l1l, trx->band_arfcn, fn);
-
- /* 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 f2ecdcc6..00000000
--- a/src/host/trxcon/sched_lchan_tchf.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (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 <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,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- const struct trx_lchan_desc *lchan_desc;
- int n_errors = -1, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint8_t l2[128], *mask;
- uint32_t *first_fn;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- first_fn = &lchan->rx_first_fn;
- 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);
-
- /* Reset internal state */
- if (bid == 0) {
- /* Clean up old measurements */
- memset(&lchan->meas, 0x00, sizeof(lchan->meas));
-
- *first_fn = fn;
- *mask = 0x00;
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Update mask and RSSI */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.num++;
-
- /* 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;
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete traffic frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
-
- /* Send BFI */
- goto bfi;
- }
-
- 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 instead of stolen TCH frame */
- 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 */
- if (n_errors < 0)
- n_errors = 116 * 4;
-
- /* 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, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- 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 (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 + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- return rc;
- }
-
- /* If we have sent the last (4/4) burst */
- if (*mask == 0x0f) {
- /* Confirm data / traffic sending */
- sched_send_dt_conf(trx, ts, lchan, 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 0201ee35..00000000
--- a/src/host/trxcon/sched_lchan_tchh.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
- * (C) 2018 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU 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 = TDMA_FN_DIFF(fn_mf, map[i][0]); \
- return 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,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- 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);
-
- /**
- * FIXME: properly update measurements
- *
- * Since TCH/H channel is using block-diagonal interleaving,
- * a single burst may carry 57 bits of one encoded frame,
- * and 57 bits of another. This should be taken into account.
- */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.num++;
-
- /* 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) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at "
- "fn=%u on %s (rc=%d)\n", 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 TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, true); /* FACCH/H */
-
- /* FACCH/H received, forward to the higher layers */
- sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-
- /* 1/2 BFI */
- goto bfi;
- } else {
- /* A good TCH frame received */
- l2_len = rc;
- }
-
- /* Calculate TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, false); /* TCH/H */
-
- /* 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 */
- if (n_errors < 0)
- n_errors = 116 * 2;
-
- /* Calculate TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, false); /* TCH/H */
-
- /* 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, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- 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 (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, 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 + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to transceiver */
- sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
-
- /* 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, 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 196f949c..00000000
--- a/src/host/trxcon/sched_lchan_xcch.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (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 <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,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- 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;
- uint32_t *first_fn;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- first_fn = &lchan->rx_first_fn;
- 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);
-
- /* Reset internal state */
- if (bid == 0) {
- /* Clean up old measurements */
- memset(&lchan->meas, 0x00, sizeof(lchan->meas));
-
- *first_fn = fn;
- *mask = 0x0;
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Update measurements */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.num++;
-
- /* 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;
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_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. */
- }
-
- /* 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", *first_fn,
- (*first_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, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- 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 (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 + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- return rc;
- }
-
- /* If we have sent the last (4/4) burst */
- if ((*mask & 0x0f) == 0x0f) {
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- /* Confirm data sending */
- sched_send_dt_conf(trx, ts, lchan, fn, false);
- }
-
- 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 50dfd6e9..00000000
--- a/src/host/trxcon/sched_prim.c
+++ /dev/null
@@ -1,611 +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,
-
- /* Measurement report */
- 0x06, 0x15, 0x36, 0x36, 0x01, 0xC0,
-
- /* TODO: Padding? Randomize if so */
- 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);
-
-#if 0
- /**
- * Update the L1 SACCH pseudo-header (only for cached MRs)
- *
- * FIXME: this would require having access to the trx_instance,
- * what can be achieved either by chain-passing the pointer
- * through sched_prim_dequeue(), or by adding some
- * back-pointers to the logical channel state.
- *
- * 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] = trx->tx_power;
- prim->payload[1] = trx->ta;
- }
-#endif
-
- /* 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 37d1acf6..00000000
--- a/src/host/trxcon/sched_trx.c
+++ /dev/null
@@ -1,704 +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_frame_clck_cb(struct trx_sched *sched)
-{
- struct trx_instance *trx = (struct trx_instance *) sched->data;
- const struct trx_frame *frame;
- struct trx_lchan_state *lchan;
- trx_lchan_tx_func *handler;
- enum trx_lchan_type chan;
- uint8_t offset, bid;
- struct trx_ts *ts;
- uint32_t fn;
- int i;
-
- /* Iterate over timeslot list */
- for (i = 0; i < TRX_TS_COUNT; i++) {
- /* Timeslot is not allocated */
- ts = trx->ts_list[i];
- if (ts == NULL)
- continue;
-
- /* Timeslot is not configured */
- if (ts->mf_layout == NULL)
- continue;
-
- /**
- * Advance frame number, giving the transceiver more
- * time until a burst must be transmitted...
- */
- fn = TDMA_FN_SUM(sched->fn_counter_proc,
- sched->fn_counter_advance);
-
- /* Get frame from multiframe */
- offset = fn % ts->mf_layout->period;
- frame = ts->mf_layout->frames + offset;
-
- /* Get required info from frame */
- 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, fn, bid);
- }
-}
-
-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);
-
- /* 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->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 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;
-
- /* Flush TS frame counter */
- ts->mf_last_fn = 0;
-
- /* 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);
-
- /* Reset internal state variables */
- lchan->rx_burst_mask = 0x00;
- lchan->tx_burst_mask = 0x00;
- lchan->rx_first_fn = 0;
-
- /* 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));
-}
-
-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,
- uint32_t fn, ubit_t *burst)
-{
- ubit_t ks[114];
- int i;
-
- /* Generate keystream for an UL burst */
- osmo_a5(lchan->a5.algo, lchan->a5.key, fn, NULL, ks);
-
- /* Apply keystream over plaintext */
- for (i = 0; i < 57; i++) {
- burst[i + 3] ^= ks[i];
- burst[i + 88] ^= ks[i + 57];
- }
-}
-
-int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
- int8_t rssi, int16_t toa256)
-{
- struct trx_lchan_state *lchan;
- const struct trx_frame *frame;
- struct trx_ts *ts;
-
- trx_lchan_rx_func *handler;
- enum trx_lchan_type chan;
- uint32_t fn, elapsed;
- uint8_t offset, bid;
-
- /* 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;
- }
-
- /* Calculate how many frames have been elapsed */
- elapsed = TDMA_FN_SUB(burst_fn, ts->mf_last_fn);
-
- /**
- * If not too many frames have been elapsed,
- * start counting from last fn + 1
- */
- if (elapsed < 10)
- fn = TDMA_FN_INC(ts->mf_last_fn);
- else
- fn = burst_fn;
-
- while (1) {
- /* 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 */
- if (!handler)
- goto next_frame;
-
- /* Find required channel state */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- goto next_frame;
-
- /* Ensure that channel is active */
- if (!lchan->active)
- goto next_frame;
-
- /* Reached current fn */
- if (fn == burst_fn) {
- /* 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, rssi, toa256);
- }
-
-next_frame:
- /* Reached current fn */
- if (fn == burst_fn)
- break;
-
- fn = TDMA_FN_INC(fn);
- }
-
- /* Set last processed frame number */
- ts->mf_last_fn = fn;
-
- return 0;
-}
-
-int sched_trx_handle_tx_burst(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, ubit_t *bits)
-{
- int rc;
-
- /* Perform A5/X burst encryption if required */
- if (lchan->a5.algo)
- sched_trx_a5_burst_enc(lchan, fn, bits);
-
- /* Forward burst to transceiver */
- rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, bits);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n");
- return rc;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h
deleted file mode 100644
index 0d424999..00000000
--- a/src/host/trxcon/sched_trx.h
+++ /dev/null
@@ -1,369 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/bits.h>
-#include <osmocom/core/utils.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_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
-};
-
-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, sbit_t *bits,
- int8_t rssi, int16_t toa256);
-
-typedef int trx_lchan_tx_func(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, uint8_t bid);
-
-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 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;
-};
-
-/* 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 Frame number of first burst */
- uint32_t rx_first_fn;
- /*! \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;
-
- struct {
- /*! \brief Number of measurements */
- unsigned int num;
- /*! \brief Sum of RSSI values */
- float rssi_sum;
- /*! \brief Sum of TOA values */
- int32_t toa256_sum;
- } meas;
-
- /*! \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;
-};
-
-struct trx_ts {
- /*! \brief Timeslot index within a frame (0..7) */
- uint8_t index;
- /*! \brief Last received frame number */
- uint32_t mf_last_fn;
-
- /*! \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;
-};
-
-/* 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 burst_fn, sbit_t *bits, uint16_t nbits,
- int8_t rssi, int16_t toa256);
-int sched_trx_handle_tx_burst(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, ubit_t *bits);
-
-/* Shared declarations for lchan handlers */
-extern const uint8_t sched_nb_training_bits[8][26];
-
-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);
-
-/* 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)
diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h
deleted file mode 100644
index 7ab17ab5..00000000
--- a/src/host/trxcon/scheduler.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <time.h>
-
-#include <osmocom/core/timer.h>
-
-#define FRAME_DURATION_uS 4615
-
-#define GSM_SUPERFRAME (26 * 51)
-#define GSM_HYPERFRAME (2048 * GSM_SUPERFRAME)
-
-/* TDMA frame number arithmetics */
-#define TDMA_FN_SUM(a, b) \
- ((a + b) % GSM_HYPERFRAME)
-#define TDMA_FN_SUB(a, b) \
- ((a + GSM_HYPERFRAME - b) % GSM_HYPERFRAME)
-#define TDMA_FN_INC(fn) \
- TDMA_FN_SUM(fn, 1)
-#define TDMA_FN_MIN(a, b) \
- (a < b ? a : b)
-#define TDMA_FN_DIFF(a, b) \
- TDMA_FN_MIN(TDMA_FN_SUB(a, b), TDMA_FN_SUB(b, a))
-
-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 */
- uint8_t 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 Frame counter */
- 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 67f770c5..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,66 +25,62 @@
*/
#include <osmocom/gsm/protocol/gsm_08_58.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,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+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, uint32_t fn, uint8_t bid);
+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,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+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, uint32_t fn, uint8_t bid);
+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,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+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, uint32_t fn, uint8_t bid);
+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,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+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, uint32_t fn, uint8_t bid);
+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,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+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, uint32_t fn, uint8_t bid);
+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",
.chan_nr = RSL_CHAN_BCCH,
@@ -91,20 +88,20 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
/* 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",
.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",
.chan_nr = RSL_CHAN_PCH_AGCH,
@@ -112,15 +109,15 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
/* 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",
.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):
@@ -133,21 +130,23 @@ 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)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
- .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):
*
- * - a traffic frame is interleaved over 6 consecutive bursts
+ * - a traffic frame is interleaved over 4 non-consecutive bursts
+ * using the even numbered bits of the first 2 bursts,
+ * and odd numbered bits of the last 2 bursts;
+ * - a FACCH/H frame is interleaved over 6 non-consecutive bursts
* using the even numbered bits of the first 2 bursts,
* all bits of the middle two 2 bursts,
* and odd numbered bits of the last 2 bursts;
@@ -157,348 +156,319 @@ 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)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .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",
.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)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
- .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_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)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
- .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_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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .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",
.chan_nr = RSL_CHAN_OSMO_PDCH,
@@ -507,44 +477,44 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
* 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",
.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",
.chan_nr = RSL_CHAN_OSMO_CBCH4,
- /* 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",
.chan_nr = RSL_CHAN_OSMO_CBCH8,
- /* 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 9eed506b..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,
- sbit_t *bits, int8_t rssi, int16_t toa256)
+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 = -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 55d70341..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,53 +411,70 @@ 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);
}
/*
- * Frequency Hopping parameters indication
+ * Frequency Hopping parameters indication.
+ *
+ * SETFH instructs transceiver to enable frequency hopping mode
+ * using the given HSN, MAIO, and Mobile Allocation parameters.
*
- * SETFH instructs transceiver to enable frequency
- * hopping mode using the given parameters.
- * CMD SETFH <HSN> <MAIO> <CH1> <CH2> [... <CHN>]
+ * CMD SETFH <HSN> <MAIO> <RXF1> <TXF1> [... <RXFN> <TXFN>]
+ *
+ * where <RXFN> and <TXFN> is a pair of Rx/Tx frequencies (in kHz)
+ * corresponding to one ARFCN the Mobile Allocation. Note that the
+ * 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)
{
- char ma_buf[100];
+ /* Reserve some room for CMD SETFH <HSN> <MAIO> */
+ char ma_buf[TRXC_BUF_SIZE - 24];
+ size_t ma_buf_len = sizeof(ma_buf) - 1;
+ uint16_t rx_freq, tx_freq;
char *ptr;
int i, rc;
- /* No channels, WTF?!? */
- if (!ma_len)
+ /* Make sure that Mobile Allocation has at least one ARFCN */
+ if (!cmdp->ma_len || cmdp->ma == NULL) {
+ LOGPFSML(trx->fi, LOGL_ERROR, "Mobile Allocation is empty?!?\n");
return -EINVAL;
+ }
- /**
- * Compose a sequence of channels (mobile allocation)
- * FIXME: the length of a CTRL command is limited to 128 symbols,
- * so we may have some problems if there are many channels...
- */
- for (i = 0, ptr = ma_buf; i < ma_len; i++) {
- /* Append a channel */
- rc = snprintf(ptr, ma_buf + sizeof(ma_buf) - ptr, "%u ", ma[i]);
- if (rc < 0)
- return rc;
+ /* Compose a sequence of Rx/Tx frequencies (mobile allocation) */
+ 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(cmdp->ma[i], 0); /* Rx: Downlink */
+ tx_freq = gsm_arfcn2freq10(cmdp->ma[i], 1); /* Tx: Uplink */
+ if (rx_freq == 0xffff || tx_freq == 0xffff) {
+ LOGPFSML(trx->fi, LOGL_ERROR, "Failed to convert ARFCN %u "
+ "to a pair of Rx/Tx frequencies\n",
+ 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 */
+ LOGPFSML(trx->fi, LOGL_ERROR, "Not enough room to encode "
+ "Mobile Allocation (N=%u)\n", cmdp->ma_len);
+ return -ENOSPC;
+ }
/* Move pointer */
+ ma_buf_len -= rc;
ptr += rc;
-
- /* Prevent buffer overflow */
- if (ptr >= (ma_buf + 100))
- return -EIO;
}
/* 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 */
@@ -446,18 +483,18 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
struct trx_instance *trx = ofd->data;
struct trx_ctrl_msg *tcm;
int resp, rsp_len;
- char buf[1500], *p;
+ char buf[TRXC_BUF_SIZE], *p;
ssize_t read_len;
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;
}
@@ -465,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;
}
@@ -482,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;
@@ -491,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);
@@ -502,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);
@@ -525,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 */
/* ------------------------------------------------------------------------ */
@@ -555,60 +633,93 @@ rsp_error:
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_instance *trx = ofd->data;
- uint8_t buf[256];
- sbit_t bits[148];
- int8_t rssi, tn;
- int16_t toa256;
- uint32_t fn;
+ struct trxcon_phyif_burst_ind bi;
+ uint8_t buf[TRXD_BUF_SIZE];
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 != 158) {
- 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 = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
- 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);
- /* Poke scheduler */
- sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa256);
+ trxcon_phyif_handle_burst_ind(trx->priv, &bi);
- /* Correct local clock counter */
- if (fn % 51 == 0)
- sched_clck_handle(&trx->sched, fn);
+ struct trxcon_phyif_rts_ind rts = {
+ .fn = GSM_TDMA_FN_SUM(bi.fn, trx->fn_advance),
+ .tn = bi.tn,
+ };
+
+ trxcon_phyif_handle_rts_ind(trx->priv, &rts);
return 0;
}
-int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
- uint8_t pwr, const ubit_t *bits)
+int trx_if_handle_phyif_burst_req(struct trx_instance *trx,
+ const struct trxcon_phyif_burst_req *br)
{
- uint8_t buf[256];
+ uint8_t buf[TRXD_BUF_SIZE];
+ size_t length;
/**
* We must be sure that we have clock,
@@ -617,55 +728,62 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
* TODO: introduce proper state machines for both
* transceiver and its TRXC interface.
*/
- if (trx->fsm->state != TRX_STATE_ACTIVE) {
- LOGP(DTRXD, LOGL_ERROR, "Ignoring TX data, "
- "transceiver isn't ready\n");
+#if 0
+ 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", tn, fn, pwr);
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
+ "TX burst tn=%u fn=%u pwr=%u\n",
+ br->tn, br->fn, br->pwr);
- buf[0] = tn;
- buf[1] = (fn >> 24) & 0xff;
- buf[2] = (fn >> 16) & 0xff;
- buf[3] = (fn >> 8) & 0xff;
- buf[4] = (fn >> 0) & 0xff;
- buf[5] = pwr;
+ buf[0] = br->tn;
+ osmo_store32be(br->fn, buf + 1);
+ buf[5] = br->pwr;
+ length = 6;
/* Copy ubits {0,1} */
- memcpy(buf + 6, bits, 148);
+ if (br->burst_len != 0) {
+ memcpy(buf + 6, br->burst, br->burst_len);
+ length += br->burst_len;
+ }
/* Send data to transceiver */
- send(trx->trx_ofd_data.fd, buf, 154, 0);
+ send(trx->trx_ofd_data.fd, buf, length, 0);
return 0;
}
/* 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;
}
@@ -673,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)) {
@@ -711,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/src/trxcon_main.c b/src/host/trxcon/src/trxcon_main.c
new file mode 100644
index 00000000..3901e336
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_main.c
@@ -0,0 +1,423 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.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-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" \
+ "There is NO WARRANTY, to the extent permitted by law.\n\n"
+
+static struct {
+ const char *debug_mask;
+ int daemonize;
+ int quit;
+
+ /* L1CTL specific */
+ unsigned int max_clients;
+ const char *bind_socket;
+
+ /* TRX specific */
+ 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 = {
+ .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;
+
+int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
+{
+ return trx_if_handle_phyif_burst_req(phyif, br);
+}
+
+int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
+{
+ return trx_if_handle_phyif_cmd(phyif, cmd);
+}
+
+void trxcon_phyif_close(void *phyif)
+{
+ trx_if_close(phyif);
+}
+
+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 void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
+{
+ struct trxcon_inst *trxcon = l1c->priv;
+
+ if (trxcon == NULL || trxcon->fi == NULL)
+ return;
+
+ osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
+}
+
+static void print_usage(const char *app)
+{
+ printf("Usage: %s\n", app);
+}
+
+static void print_help(void)
+{
+ printf(" Some help...\n");
+ printf(" -h --help this text\n");
+ 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 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'},
+ {"debug", 1, 0, 'd'},
+ {"socket", 1, 0, 's'},
+ {"trx-bind", 1, 0, 'b'},
+ /* NOTE: 'trx-ip' is now an alias for 'trx-remote'
+ * due to backward compatibility reasons! */
+ {"trx-ip", 1, 0, 'i'},
+ {"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:F:s:g:C:Dh",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ errno = 0;
+
+ switch (c) {
+ case 'h':
+ print_usage(argv[0]);
+ print_help();
+ exit(0);
+ break;
+ case 'd':
+ app_data.debug_mask = optarg;
+ break;
+ case 'b':
+ app_data.trx_bind_ip = optarg;
+ break;
+ case 'i':
+ app_data.trx_remote_ip = optarg;
+ break;
+ case 'p':
+ 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 = 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;
+ break;
+ 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;
+ default:
+ break;
+ }
+ }
+}
+
+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:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct l1ctl_server_cfg server_cfg;
+ struct l1ctl_server *server = NULL;
+ int rc = 0;
+
+ printf("%s", COPYRIGHT);
+ handle_options(argc, argv);
+
+ /* Track the use of talloc NULL memory contexts */
+ talloc_enable_null_tracking();
+
+ /* Init talloc memory management system */
+ tall_trxcon_ctx = talloc_init("trxcon context");
+ msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
+
+ /* 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 */
+ trxcon_logging_init(tall_trxcon_ctx, app_data.debug_mask);
+
+ /* Configure pretty logging */
+ log_set_print_extended_timestamp(osmo_stderr_target, 1);
+ 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);
+
+ 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) {
+ 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;
+ }
+
+ 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);
+ }
+
+ /* Suppress ICMP "destination unreachable" errors */
+ gsmtap_source_add_sink(app_data.gsmtap);
+ }
+
+ /* 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");
+
+ if (app_data.daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ goto exit;
+ }
+ }
+
+ /* Initialize pseudo-random generator */
+ srand(time(NULL));
+
+ while (!app_data.quit)
+ osmo_select_main(0);
+
+exit:
+ if (server != NULL)
+ l1ctl_server_free(server);
+
+ /* Deinitialize logging */
+ log_fini();
+
+ /**
+ * Print report for the root talloc context in order
+ * to be able to find and fix potential memory leaks.
+ */
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ talloc_free(tall_trxcon_ctx);
+
+ /* Make both Valgrind and ASAN happy */
+ talloc_report_full(NULL, stderr);
+ talloc_disable_null_tracking();
+
+ return rc;
+}
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 a44600d9..00000000
--- a/src/host/trxcon/trx_if.h
+++ /dev/null
@@ -1,80 +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"
-
-/* 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[128];
- 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, uint8_t tn, uint32_t fn,
- uint8_t pwr, const ubit_t *bits);
diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c
deleted file mode 100644
index 8e371df1..00000000
--- a/src/host/trxcon/trxcon.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- *
- * (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 <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <signal.h>
-#include <time.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/signal.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/application.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"
-
-#define COPYRIGHT \
- "Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>\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 struct {
- const char *debug_mask;
- int daemonize;
- int quit;
-
- /* L1CTL specific */
- struct l1ctl_link *l1l;
- 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;
-} app_data;
-
-static void *tall_trxcon_ctx = NULL;
-struct osmo_fsm_inst *trxcon_fsm;
-
-static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- if (event == L1CTL_EVENT_CONNECT)
- osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_MANAGED, 0, 0);
-}
-
-static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- switch (event) {
- case L1CTL_EVENT_DISCONNECT:
- osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0);
-
- if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) {
- /* Reset scheduler and clock counter */
- sched_trx_reset(app_data.trx, true);
-
- /* 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);
- }
-}
-
-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 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 }
-};
-
-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,
-};
-
-static void print_usage(const char *app)
-{
- printf("Usage: %s\n", app);
-}
-
-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(" -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 Scheduler clock advance (default 20)\n");
- printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n");
- printf(" -D --daemonize Run as daemon\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"socket", 1, 0, 's'},
- {"trx-bind", 1, 0, 'b'},
- /* NOTE: 'trx-ip' is now an alias for 'trx-remote'
- * due to backward compatibility reasons! */
- {"trx-ip", 1, 0, 'i'},
- {"trx-remote", 1, 0, 'i'},
- {"trx-port", 1, 0, 'p'},
- {"trx-advance", 1, 0, 'f'},
- {"daemonize", 0, 0, 'D'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "d:b:i:p:f:s:Dh",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage(argv[0]);
- print_help();
- exit(0);
- break;
- case 'd':
- app_data.debug_mask = optarg;
- break;
- case 'b':
- app_data.trx_bind_ip = optarg;
- break;
- case 'i':
- app_data.trx_remote_ip = optarg;
- break;
- case 'p':
- app_data.trx_base_port = atoi(optarg);
- break;
- case 'f':
- app_data.trx_fn_advance = atoi(optarg);
- break;
- case 's':
- app_data.bind_socket = optarg;
- break;
- case 'D':
- app_data.daemonize = 1;
- break;
- default:
- break;
- }
- }
-}
-
-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 = 20;
-
- app_data.debug_mask = NULL;
- app_data.daemonize = 0;
- app_data.quit = 0;
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stderr, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- app_data.quit++;
- break;
- case SIGABRT:
- case SIGUSR1:
- case SIGUSR2:
- talloc_report_full(tall_trxcon_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-int main(int argc, char **argv)
-{
- int rc = 0;
-
- printf("%s", COPYRIGHT);
- init_defaults();
- handle_options(argc, argv);
-
- /* Track the use of talloc NULL memory contexts */
- talloc_enable_null_tracking();
-
- /* Init talloc memory management system */
- tall_trxcon_ctx = talloc_init("trxcon context");
- msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
-
- /* Setup signal handlers */
- signal(SIGINT, &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);
-
- /* Allocate the application state machine */
- osmo_fsm_register(&trxcon_fsm_def);
- 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;
-
- /* Bind L1CTL with TRX and vice versa */
- app_data.l1l->trx = app_data.trx;
- app_data.trx->l1l = app_data.l1l;
-
- /* Init scheduler */
- rc = sched_trx_init(app_data.trx, app_data.trx_fn_advance);
- if (rc)
- goto exit;
-
- LOGP(DAPP, LOGL_NOTICE, "Init complete\n");
-
- if (app_data.daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- goto exit;
- }
- }
-
- /* Initialize pseudo-random generator */
- srand(time(NULL));
-
- while (!app_data.quit)
- 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);
-
- /* Deinitialize logging */
- log_fini();
-
- /**
- * Print report for the root talloc context in order
- * to be able to find and fix potential memory leaks.
- */
- talloc_report_full(tall_trxcon_ctx, stderr);
- talloc_free(tall_trxcon_ctx);
-
- /* Make both Valgrind and ASAN happy */
- talloc_report_full(NULL, stderr);
- talloc_disable_null_tracking();
-
- return rc;
-}
diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h
deleted file mode 100644
index f66a6285..00000000
--- a/src/host/trxcon/trxcon.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#define GEN_MASK(state) (0x01 << state)
-
-extern struct osmo_fsm_inst *trxcon_fsm;
-
-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 c8012c99..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])])
@@ -12,6 +14,8 @@ AC_PROG_CC
AC_PROG_INSTALL
dnl checks for libraries
+dnl TODO: insert libosmocore version with GSMTAP_CHANNEL_VOICE: PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.4.0)
+dnl (at time of writing not released yet)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm)
@@ -20,9 +24,45 @@ AC_HEADER_STDC
dnl Checks for typedefs, structures and compiler characteristics
+AC_ARG_ENABLE(sanitize,
+ [AS_HELP_STRING(
+ [--enable-sanitize],
+ [Compile with address sanitizer enabled],
+ )], [sanitize=$enableval], [sanitize="no"])
+if test x"$sanitize" = x"yes"
+then
+ CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
+ 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 67c24bb6..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
};
@@ -27,7 +27,7 @@ struct l1_cell_info {
uint8_t bsic;
/* Combined or non-combined CCCH */
uint8_t ccch_mode; /* enum ccch_mode */
- /* whats the delta of the cells current GSM frame number
+ /* what's the delta of the cells current GSM frame number
* compared to our current local frame number */
int32_t fn_offset;
/* how much does the TPU need adjustment (delta) to synchronize
@@ -68,22 +68,14 @@ struct l1_state_ms {
struct {
uint8_t chan_type; // like rsl chantype 08.58 -> Chapter 9.3.1 */
+ uint16_t band_arfcn;
uint8_t tn; // timeslot number 1-7
uint8_t subslot; // subslot of the dedicated channel, SDCCH/4:[0-3], SDCCH/8:[0-7]
- uint8_t scn; // single-hop cellular network? (ununsed in virtual um)
- uint8_t tsc; // training sequence code (ununsed in virtual um)
- uint8_t h; // hopping enabled flag (ununsed in virtual um)
+ uint8_t scn; // single-hop cellular network? (unused in virtual um)
+ 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 {
@@ -99,6 +91,12 @@ struct l1_state_ms {
uint8_t arfcn_sig_lev_red_dbm[1024];
struct osmo_timer_list arfcn_sig_lev_timers[1024];
} meas;
+ struct {
+ uint16_t band_arfcn_from;
+ uint16_t band_arfcn_to;
+ /* timer between receiving PM_REQ and responding with PM_CONF */
+ struct osmo_timer_list timer;
+ } req;
} pm;
};
@@ -108,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 52f2df64..fe060929 100644
--- a/src/host/virt_phy/include/virtphy/virtual_um.h
+++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virtual_um.h
@@ -17,7 +17,9 @@
#define VIRT_UM_MSGB_SIZE 256
#define DEFAULT_MS_MCAST_GROUP "239.193.23.1"
+#define DEFAULT_MS_MCAST_PORT 4729 /* IANA-registered port for GSMTAP */
#define DEFAULT_BTS_MCAST_GROUP "239.193.23.2"
+#define DEFAULT_BTS_MCAST_PORT 4729 /* IANA-registered port for GSMTAP */
struct virt_um_inst {
void *priv;
@@ -27,7 +29,7 @@ struct virt_um_inst {
struct virt_um_inst *virt_um_init(
void *ctx, char *tx_mcast_group, uint16_t tx_mcast_port,
- char *rx_mcast_group, uint16_t rx_mcast_port,
+ char *rx_mcast_group, uint16_t rx_mcast_port, int ttl, const char *dev_name,
void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg));
void virt_um_destroy(struct virt_um_inst *vui);
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 94174da4..00000000
--- a/src/host/virt_phy/include/virtphy/l1ctl_sap.h
+++ /dev/null
@@ -1,80 +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_rx_from_l23(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 2cf9d2dc..ef8d6840 100644
--- a/src/host/virt_phy/src/gsmtapl1_if.c
+++ b/src/host/virt_phy/src/gsmtapl1_if.c
@@ -20,25 +20,28 @@
*
*/
+#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>
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm_utils.h>
#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)
{
@@ -48,6 +51,29 @@ static char *pseudo_lchan_name(uint16_t arfcn, uint8_t ts, uint8_t ss, uint8_t s
return lname;
}
+/* Return gsmtap_um_voice_type or -1 on error */
+static int get_um_voice_type(enum gsm48_chan_mode tch_mode, uint8_t rsl_chantype)
+{
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ switch (rsl_chantype) {
+ case RSL_CHAN_Bm_ACCHs:
+ return GSMTAP_UM_VOICE_FR;
+ case RSL_CHAN_Lm_ACCHs:
+ return GSMTAP_UM_VOICE_HR;
+ default:
+ return -1;
+ }
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ return GSMTAP_UM_VOICE_EFR;
+ case GSM48_CMODE_SPEECH_AMR:
+ return GSMTAP_UM_VOICE_AMR;
+ default:
+ return -1;
+ }
+}
+
/**
* Replace l11 header of given msgb by a gsmtap header and send it over the virt um.
*/
@@ -57,8 +83,8 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn
struct l1ctl_info_ul *ul;
struct gsmtap_hdr *gh;
struct msgb *outmsg; /* msg to send with gsmtap header prepended */
- uint16_t arfcn = ms->state.serving_cell.arfcn; /* arfcn of the cell we currently camp on */
- uint8_t signal_dbm = 63; /* signal strength */
+ uint16_t arfcn;
+ 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 */
@@ -68,18 +94,34 @@ 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 */
+ if (ms->state.state == MS_STATE_DEDICATED)
+ arfcn = ms->state.dedicated.band_arfcn;
+ else
+ arfcn = ms->state.serving_cell.arfcn;
+
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_rsl2gsmtap(rsl_chantype, 0);
+ break;
+ case L1CTL_TRAFFIC_REQ:
+ ul = (struct l1ctl_info_ul *)l1h->data;
+ rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
+ gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, 0, true);
+ /* the first byte indicates the type of voice codec (gsmtap_um_voice_type);
+ * let's first strip any data in front of the l2 header, then push this extra
+ * byte to the front and finally adjust the l2h pointer */
+ msgb_pull_to_l2(msg);
+ msgb_push_u8(msg, get_um_voice_type(ms->state.tch_mode, rsl_chantype));
+ msg->l2h = msg->data;
+ data = msgb_l2(msg);
+ data_len = msgb_l2len(msg);
break;
default:
ul = (struct l1ctl_info_ul *)l1h->data;
rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
- gsmtap_chan = chantype_rsl2gsmtap(rsl_chantype, ul->link_id);
+ gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, ul->link_id, false);
break;
}
@@ -116,100 +158,34 @@ 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 adressed
- * 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);
- /* we do not forward messages to l23 if we are in network search state */
- if (ms->state.state == MS_STATE_IDLE_SEARCHING)
+ switch (ms->state.state) {
+ case MS_STATE_IDLE_SEARCHING:
+ /* we do not forward messages to l23 if we are in network search state */
return;
-
- /* forward downlink msg to fbsb sync routine if we are in sync state */
- if (ms->state.state == MS_STATE_IDLE_SYNCING) {
+ case MS_STATE_IDLE_SYNCING:
+ /* forward downlink msg to fbsb sync routine if we are in sync state */
prim_fbsb_sync(ms, msg);
return;
- }
- /* generally ignore all messages coming from another arfcn than the camped one */
- if (ms->state.serving_cell.arfcn != arfcn) {
- return;
+ case MS_STATE_DEDICATED:
+ /* generally ignore all messages coming from another arfcn than the camped one */
+ if (arfcn != ms->state.dedicated.band_arfcn)
+ return;
+ break;
+ default:
+ /* generally ignore all messages coming from another arfcn than the camped one */
+ if (arfcn != ms->state.serving_cell.arfcn)
+ return;
+ break;
}
virt_l1_sched_sync_time(ms, ms->state.downlink_time, 0);
@@ -219,44 +195,49 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) {
case GSMTAP_CHANNEL_TCH_H:
case GSMTAP_CHANNEL_TCH_F:
-#if 0
- /* TODO: handle voice */
- if (!facch && !tch_acch) {
- l1ctl_tx_traffic_ind(msg, arfcn, link_id, chan_nr, fn,
- snr, signal_dbm, 0, 0);
- }
-#endif
+ /* This is TCH signalling, for voice frames see GSMTAP_CHANNEL_VOICE */
case GSMTAP_CHANNEL_SDCCH4:
case GSMTAP_CHANNEL_SDCCH8:
/* only forward messages on dedicated channels to l2, if
* 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:
+ case GSMTAP_CHANNEL_VOICE_H:
+ /* only forward messages on dedicated channels to l2, if
+ * the timeslot and subslot is fitting */
+ 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, rxlev, 0, 0);
+ }
+ break;
+ case GSMTAP_CHANNEL_CBCH51:
+ /* only pass CBCH data if the user application actually indicated that a CBCH
+ * is present */
+ if (ms->state.serving_cell.ccch_mode != CCCH_MODE_COMBINED_CBCH)
+ break;
case GSMTAP_CHANNEL_AGCH:
case GSMTAP_CHANNEL_PCH:
case GSMTAP_CHANNEL_BCCH:
- case GSMTAP_CHANNEL_CBCH51:
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 f95a4caf..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;
}
@@ -286,14 +284,22 @@ void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg)
rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
- LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_DM_EST_REQ (chan_nr=0x%02x, tn=%u, ss=%u)\n",
- ul->chan_nr, timeslot, subslot);
+ LOGPMS(DL1C, LOGL_DEBUG, ms, "Rx L1CTL_DM_EST_REQ (chan_nr=0x%02x, arfcn=%u, tn=%u, ss=%u)\n",
+ ul->chan_nr, ntohs(est_req->h0.band_arfcn), timeslot, subslot);
+ OSMO_ASSERT(est_req->h == 0); /* we don't do hopping */
+ ms->state.dedicated.band_arfcn = ntohs(est_req->h0.band_arfcn);
ms->state.dedicated.chan_type = rsl_chantype;
ms->state.dedicated.tn = timeslot;
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;
@@ -313,7 +319,7 @@ void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg)
*
* Handle frequency change in dedicated mode. E.g. used for frequency hopping.
*
- * Note: Not needed for virtual physical layer as freqency hopping is generally disabled.
+ * Note: Not needed for virtual physical layer as frequency hopping is generally disabled.
*/
void l1ctl_rx_dm_freq_req(struct l1_model_ms *ms, struct msgb *msg)
{
@@ -378,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 */
}
@@ -426,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:
@@ -484,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);
@@ -504,7 +516,7 @@ void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg)
* The neighbor cell description is one of the info messages sent by the BTS on BCCH.
* This method will also enable neighbor measurement in the multiframe scheduler.
*
- * Note: Not needed for virtual physical layer as we dont maintain neigbors.
+ * Note: Not needed for virtual physical layer as we don't maintain neighbors.
*/
void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg)
{
@@ -543,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 bf28895f..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
@@ -72,7 +72,7 @@ static int l1ctl_sock_data_cb(struct osmo_fd *ofd, unsigned int what)
int rc;
/* Check if request is really read request */
- if (!(what & BSC_FD_READ))
+ if (!(what & OSMO_FD_READ))
return 0;
msg = msgb_alloc(L1CTL_SOCK_MSGB_SIZE, "L1CTL sock rx");
@@ -125,10 +125,7 @@ static int l1ctl_sock_accept_cb(struct osmo_fd *ofd, unsigned int what)
}
lsc->l1ctl_sock = lsi;
- lsc->ofd.fd = fd;
- lsc->ofd.when = BSC_FD_READ;
- lsc->ofd.cb = l1ctl_sock_data_cb;
- lsc->ofd.data = lsc;
+ osmo_fd_setup(&lsc->ofd, fd, OSMO_FD_READ, l1ctl_sock_data_cb, lsc, 0);
if (lsi->accept_cb) {
rc = lsi->accept_cb(lsc);
if (rc < 0) {
@@ -163,9 +160,7 @@ struct l1ctl_sock_inst *l1ctl_sock_init(
lsi = talloc_zero(ctx, struct l1ctl_sock_inst);
lsi->priv = NULL;
- lsi->ofd.data = lsi;
- lsi->ofd.when = BSC_FD_READ;
- lsi->ofd.cb = l1ctl_sock_accept_cb;
+ osmo_fd_setup(&lsi->ofd, -1, OSMO_FD_READ, l1ctl_sock_accept_cb, lsi, 0);
rc = osmo_sock_unix_init_ofd(&lsi->ofd, SOCK_STREAM, 0, path, OSMO_SOCK_F_BIND);
if (rc < 0) {
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 7e4e79b1..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_DEBUG,
+ .loglevel = LOGL_NOTICE,
},
[DL1P] = {
.name = "DL1P",
.description = "Layer 1 Data",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_DEBUG,
+ .loglevel = LOGL_NOTICE,
},
[DVIRPHY] = {
.name = "DVIRPHY",
.description = "Virtual Layer 1 Interface",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_DEBUG,
+ .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_DEBUG,
+ .loglevel = LOGL_NOTICE,
},
};
@@ -104,24 +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_filename(stderr_target, 1);
- log_set_use_color(stderr_target, 0);
- log_set_print_timestamp(stderr_target, 1);
- 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;
}
@@ -131,5 +131,5 @@ const char *getL1ctlPrimName(uint8_t type)
if (type < ARRAY_SIZE(l1ctlPrimNames))
return l1ctlPrimNames[type];
else
- return "Unknwon Primitive";
+ return "Unknown Primitive";
}
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 9a713fcf..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
@@ -43,9 +45,7 @@ int mcast_client_sock_setup(struct osmo_fd *ofd, const char *mcast_group, uint16
int rc;
unsigned int flags = OSMO_SOCK_F_BIND | OSMO_SOCK_F_NO_MCAST_ALL | OSMO_SOCK_F_UDP_REUSEADDR;
- ofd->cb = fd_rx_cb;
- ofd->when = BSC_FD_READ;
- ofd->data = osmo_fd_data;
+ osmo_fd_setup(ofd, -1, OSMO_FD_READ, fd_rx_cb, osmo_fd_data, 0);
/* Create mcast client socket */
rc = osmo_sock_init_ofd(ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
diff --git a/src/host/virt_phy/src/shared/virtual_um.c b/src/host/virt_phy/src/shared/virtual_um.c
index 9415bfbb..e55bb034 100644
--- a/src/host/virt_phy/src/shared/virtual_um.c
+++ b/src/host/virt_phy/src/shared/virtual_um.c
@@ -19,15 +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 <osmocom/bb/virtphy/osmo_mcast_sock.h>
+#include <osmocom/bb/virtphy/virtual_um.h>
/**
* Virtual UM interface file descriptor callback.
@@ -37,49 +40,71 @@ static int virt_um_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct virt_um_inst *vui = ofd->data;
- // check if the read flag is set
- if (what & BSC_FD_READ) {
- // allocate message buffer of specified size
- struct msgb *msg = msgb_alloc(VIRT_UM_MSGB_SIZE,
- "Virtual UM Rx");
+ if (what & OSMO_FD_READ) {
+ struct msgb *msg = msgb_alloc(VIRT_UM_MSGB_SIZE, "Virtual UM Rx");
int rc;
- // read message from fd in message buffer
- rc = mcast_bidir_sock_rx(vui->mcast_sock, msgb_data(msg),
- msgb_tailroom(msg));
- // rc is number of bytes actually read
+ /* read message from fd into message buffer */
+ rc = mcast_bidir_sock_rx(vui->mcast_sock, msgb_data(msg), msgb_tailroom(msg));
if (rc > 0) {
msgb_put(msg, rc);
msg->l1h = msgb_data(msg);
- // call the l1 callback function for a received msg
+ /* call the l1 callback function for a received msg */
vui->recv_cb(vui, msg);
- } else {
- // TODO: this kind of error handling might be a bit harsh
+ } else if (rc == 0) {
vui->recv_cb(vui, NULL);
- // Unregister fd from select loop
- osmo_fd_unregister(ofd);
- close(ofd->fd);
- ofd->fd = -1;
- ofd->when = 0;
- }
+ osmo_fd_close(ofd);
+ } else
+ perror("Read from multicast socket");
+
}
return 0;
}
-struct virt_um_inst *virt_um_init(
- void *ctx, char *tx_mcast_group, uint16_t tx_mcast_port,
- char *rx_mcast_group, uint16_t rx_mcast_port,
- void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg))
+struct virt_um_inst *virt_um_init(void *ctx, char *tx_mcast_group, uint16_t tx_mcast_port,
+ char *rx_mcast_group, uint16_t rx_mcast_port, int ttl, const char *dev_name,
+ void (*recv_cb)(struct virt_um_inst *vui, struct msgb *msg))
{
struct virt_um_inst *vui = talloc_zero(ctx, struct virt_um_inst);
- vui->mcast_sock = mcast_bidir_sock_setup(ctx, tx_mcast_group,
- tx_mcast_port, rx_mcast_group, rx_mcast_port, 1,
- virt_um_fd_cb, vui);
+ int rc;
+
+ vui->mcast_sock = mcast_bidir_sock_setup(ctx, tx_mcast_group, tx_mcast_port,
+ rx_mcast_group, rx_mcast_port, 1, virt_um_fd_cb, vui);
+ if (!vui->mcast_sock) {
+ perror("Unable to create VirtualUm multicast socket");
+ talloc_free(vui);
+ return NULL;
+ }
vui->recv_cb = recv_cb;
+ if (ttl >= 0) {
+ rc = osmo_sock_mcast_ttl_set(vui->mcast_sock->tx_ofd.fd, ttl);
+ if (rc < 0) {
+ perror("Cannot set TTL of Virtual Um transmit socket");
+ goto out_close;
+ }
+ }
+
+ if (dev_name) {
+ rc = osmo_sock_mcast_iface_set(vui->mcast_sock->tx_ofd.fd, dev_name);
+ if (rc < 0) {
+ perror("Cannot bind multicast tx to given device");
+ goto out_close;
+ }
+ rc = osmo_sock_mcast_iface_set(vui->mcast_sock->rx_ofd.fd, dev_name);
+ if (rc < 0) {
+ perror("Cannot bind multicast rx to given device");
+ goto out_close;
+ }
+ }
+
return vui;
+out_close:
+ mcast_bidir_sock_close(vui->mcast_sock);
+ talloc_free(vui);
+ return NULL;
}
void virt_um_destroy(struct virt_um_inst *vui)
@@ -97,6 +122,8 @@ int virt_um_write_msg(struct virt_um_inst *vui, struct msgb *msg)
rc = mcast_bidir_sock_tx(vui->mcast_sock, msgb_data(msg),
msgb_length(msg));
+ if (rc < 0)
+ rc = -errno;
msgb_free(msg);
return rc;
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 4737135c..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
*/
@@ -97,6 +99,7 @@ void virt_l1_sched_execute(struct l1_model_ms *ms, uint32_t fn)
ti_next->handler_cb(ms, mi_next->fn, ti_next->ts, ti_next->msg);
/* remove handled tdma sched item */
llist_del(&ti_next->tdma_item_entry);
+ talloc_free(ti_next);
}
/* remove handled mframe sched item */
llist_del(&mi_next->mframe_item_entry);
@@ -129,12 +132,8 @@ void virt_l1_sched_schedule(struct l1_model_ms *ms, struct msgb *msg, uint32_t f
/* list did not contain mframe item with needed fn */
mi_fn = talloc_zero(ms, struct virt_l1_sched_mframe_item);
mi_fn->fn = fn;
- /* need to manually init the struct content.... no so happy */
- mi_fn->tdma_item_list.prev = &mi_fn->tdma_item_list;
- mi_fn->tdma_item_list.next = &mi_fn->tdma_item_list;
-
- /* TODO: check if we get an error if list is empty... */
- llist_add(&mi_fn->mframe_item_entry, mi_next->mframe_item_entry.prev);
+ INIT_LLIST_HEAD(&mi_fn->tdma_item_list);
+ llist_add_tail(&mi_fn->mframe_item_entry, &mi_next->mframe_item_entry);
}
ti_new = talloc_zero(mi_fn, struct virt_l1_sched_tdma_item);
diff --git a/src/host/virt_phy/src/virt_prim_data.c b/src/host/virt_phy/src/virt_prim_data.c
index 96534aab..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.
@@ -47,7 +43,8 @@
static void virt_l1_sched_handler_cb(struct l1_model_ms *ms, uint32_t fn, uint8_t tn, struct msgb * msg)
{
gsmtapl1_tx_to_virt_um_inst(ms, fn, tn, msg);
- l1ctl_tx_data_conf(ms, fn, 0, ms->state.serving_cell.arfcn);
+ /* FIXME: get ARFCN from msg payload */
+ l1ctl_tx_data_conf(ms, fn, 0, ms->state.dedicated.band_arfcn);
}
/**
@@ -73,15 +70,15 @@ void l1ctl_rx_data_req(struct l1_model_ms *ms, struct msgb *msg)
rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
msg->l2h = data_ind->data;
- LOGPMS(DL1P, LOGL_INFO, ms, "Rx L1CTL_DATA_REQ (chan_nr=0x%02x, link_id=0x%02x) %s\n",
+ LOGPMS(DL1P, LOGL_DEBUG, ms, "Rx L1CTL_DATA_REQ (chan_nr=0x%02x, link_id=0x%02x) %s\n",
ul->chan_nr, ul->link_id, osmo_hexdump(msg->l2h, msgb_l2len(msg)));
virt_l1_sched_schedule(ms, msg, fn_sched, timeslot, &virt_l1_sched_handler_cb);
}
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;
@@ -97,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;
@@ -105,7 +102,7 @@ void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn,
memcpy(l1di->data, msgb_data(msg), msgb_length(msg));
- LOGPMS(DL1P, LOGL_INFO, ms, "TX L1CTL_DATA_IND (link_id=0x%02x) %s\n", link_id,
+ LOGPMS(DL1P, LOGL_DEBUG, ms, "TX L1CTL_DATA_IND (link_id=0x%02x) %s\n", link_id,
osmo_hexdump(msgb_data(msg), msgb_length(msg)));
l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
}
@@ -123,6 +120,6 @@ void l1ctl_tx_data_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint1
struct msgb * l1ctl_msg;
l1ctl_msg = l1ctl_create_l2_msg(L1CTL_DATA_CONF, fn, snr, arfcn);
/* send confirm to layer23 */
- LOGPMS(DL1P, LOGL_INFO, ms, "Tx L1CTL_DATA_CONF\n");
+ LOGPMS(DL1P, LOGL_DEBUG, ms, "Tx L1CTL_DATA_CONF\n");
l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
}
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 46370138..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.
@@ -84,38 +81,49 @@ void l1ctl_rx_pm_req(struct l1_model_ms *ms, struct msgb *msg)
struct l1_state_ms *l1s = &ms->state;
struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data;
- struct msgb *resp_msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
- uint16_t arfcn_next;
- /* convert to host order */
- pm_req->range.band_arfcn_from = ntohs(pm_req->range.band_arfcn_from);
- pm_req->range.band_arfcn_to = ntohs(pm_req->range.band_arfcn_to);
+ /* just parse the data from the request here */
+ l1s->pm.req.band_arfcn_from = ntohs(pm_req->range.band_arfcn_from);
+ l1s->pm.req.band_arfcn_to = ntohs(pm_req->range.band_arfcn_to);
- LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_PM_REQ TYPE=%u, FROM=%d, TO=%d\n",
- pm_req->type, pm_req->range.band_arfcn_from, pm_req->range.band_arfcn_to);
+ LOGPMS(DL1C, LOGL_DEBUG, ms, "Rx L1CTL_PM_REQ TYPE=%u, FROM=%d, TO=%d\n",
+ pm_req->type, l1s->pm.req.band_arfcn_from, l1s->pm.req.band_arfcn_to);
+
+ /* generating the response will happen delayed in a timer, as otherwise
+ * we will respond too fast, and 'mobile' will run havoc in a busy loop issuing
+ * endless PM_REQ until a cell eventually isfound */
+ osmo_timer_schedule(&l1s->pm.req.timer, 0, 300000);
+}
+
+static void pm_conf_timer_cb(void *data)
+{
+ struct l1_model_ms *ms = data;
+ struct l1_state_ms *l1s = &ms->state;
+ struct msgb *resp_msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
+ uint16_t arfcn_next;
- for (arfcn_next = pm_req->range.band_arfcn_from;
- arfcn_next <= pm_req->range.band_arfcn_to; ++arfcn_next) {
+ for (arfcn_next = l1s->pm.req.band_arfcn_from;
+ arfcn_next <= l1s->pm.req.band_arfcn_to; ++arfcn_next) {
struct l1ctl_pm_conf *pm_conf = (struct l1ctl_pm_conf *) msgb_put(resp_msg, sizeof(*pm_conf));
pm_conf->band_arfcn = htons(arfcn_next);
/* set min and max to the value calculated for that
* arfcn (IGNORE UPLINKK AND PCS AND OTHER FLAGS) */
pm_conf->pm[0] = dbm2rxlev(l1s->pm.meas.arfcn_sig_lev_dbm[arfcn_next & ARFCN_NO_FLAGS_MASK]);
pm_conf->pm[1] = dbm2rxlev(l1s->pm.meas.arfcn_sig_lev_dbm[arfcn_next & ARFCN_NO_FLAGS_MASK]);
- if (arfcn_next == pm_req->range.band_arfcn_to) {
+ if (arfcn_next == l1s->pm.req.band_arfcn_to) {
struct l1ctl_hdr *resp_l1h = msgb_l1(resp_msg);
resp_l1h->flags |= L1CTL_F_DONE;
}
- /* no more space to hold mor pm info in msgb, flush to l23 */
+ /* no more space to hold more pm info in msgb, flush to l23 */
if (msgb_tailroom(resp_msg) < sizeof(*pm_conf)) {
- LOGPMS(DL1C, LOGL_INFO, ms, "Tx L1CTL_PM_CONF\n");
+ LOGPMS(DL1C, LOGL_DEBUG, ms, "Tx L1CTL_PM_CONF\n");
l1ctl_sap_tx_to_l23_inst(ms, resp_msg);
resp_msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
}
}
/* transmit the remaining part of pm response to l23 */
if (resp_msg) {
- LOGPMS(DL1C, LOGL_INFO, ms, "Tx L1CTL_PM_CONF\n");
+ LOGPMS(DL1C, LOGL_DEBUG, ms, "Tx L1CTL_PM_CONF\n");
l1ctl_sap_tx_to_l23_inst(ms, resp_msg);
}
}
@@ -137,6 +145,7 @@ void prim_pm_init(struct l1_model_ms *model)
l1s->pm.meas.arfcn_sig_lev_timers[i].cb = prim_pm_timer_cb;
l1s->pm.meas.arfcn_sig_lev_timers[i].data = &l1s->pm.meas.arfcn_sig_lev_dbm[i];
}
+ osmo_timer_setup(&l1s->pm.req.timer, pm_conf_timer_cb, model);
}
void prim_pm_exit(struct l1_model_ms *model)
@@ -146,4 +155,5 @@ void prim_pm_exit(struct l1_model_ms *model)
for (i = 0; i < 1024; ++i)
osmo_timer_del(&l1s->pm.meas.arfcn_sig_lev_timers[i]);
+ osmo_timer_del(&l1s->pm.req.timer);
}
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 5f6b273b..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.
@@ -47,7 +43,8 @@
static void virt_l1_sched_handler_cb(struct l1_model_ms *ms, uint32_t fn, uint8_t tn, struct msgb * msg)
{
gsmtapl1_tx_to_virt_um_inst(ms, fn, tn, msg);
- l1ctl_tx_traffic_conf(ms, fn, 0, ms->state.serving_cell.arfcn);
+ /* FIXME: get ARFCN from msg payload */
+ l1ctl_tx_traffic_conf(ms, fn, 0, ms->state.dedicated.band_arfcn);
}
/**
@@ -78,13 +75,14 @@ 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;
struct l1ctl_traffic_ind * l1ti;
struct l1ctl_info_dl * l1dl;
- uint8_t *frame, frame_len;
+ uint8_t *frame;
+ int frame_len;
uint8_t rsl_chan_type, subchan, timeslot;
l1ctl_msg = l1ctl_msgb_alloc(L1CTL_TRAFFIC_IND);
l1dl = (struct l1ctl_info_dl *) msgb_put(l1ctl_msg, sizeof(*l1dl));
@@ -97,15 +95,20 @@ 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;
- /* TODO: traffic decoding and decryption */
-
- frame_len = msgb_length(msg);
+ /* The first byte indicates the type of voice frame (enum gsmtap_um_voice_type),
+ * which we simply ignore here and pass on the frame without that byte.
+ * TODO: Check for consistency with ms->state.tch_mode ? */
+ frame_len = msgb_length(msg) - 1;
+ if (frame_len < 0) {
+ msgb_free(l1ctl_msg);
+ return;
+ }
frame = (uint8_t *) msgb_put(l1ctl_msg, frame_len);
- memcpy(frame, msgb_data(msg), frame_len);
+ memcpy(frame, msgb_data(msg)+1, frame_len);
DEBUGPMS(DL1P, ms, "Tx L1CTL_TRAFFIC_IND (chan_nr=0x%02x, link_id=0x%02x)\n", chan_nr, link_id);
l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
diff --git a/src/host/virt_phy/src/virtphy.c b/src/host/virt_phy/src/virtphy.c
index d0a2ddbf..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 */
@@ -59,13 +62,15 @@ static char *log_mask = DEFAULT_LOG_MASK;
static char *l1ctl_sock_path = L1CTL_SOCK_PATH;
static char *arfcn_sig_lev_red_mask = NULL;
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");
@@ -76,6 +81,8 @@ static void print_help()
printf(" -s --l1ctl-sock l1ctl socket path path.\n");
printf(" -r --arfcn-sig-lev-red reduce signal level (e.g. 666,12:888,43:176,22).\n");
printf(" -t --pm-timeout power management timeout.\n");
+ printf(" -T --mcast-ttl TTL set TTL of Virtual Um GSMTAP multicast frames\n");
+ printf(" -D --mcast-deav NETDEV bind to given network device for Virtual Um\n");
}
static void handle_options(int argc, char **argv)
@@ -91,9 +98,11 @@ static void handle_options(int argc, char **argv)
{"l1ctl-sock", required_argument, 0, 's'},
{"arfcn-sig-lev-red", required_argument, 0, 'r'},
{"pm-timeout", required_argument, 0, 't'},
+ {"mcast-ttl", required_argument, 0, 'T'},
+ {"mcast-dev", required_argument, 0, 'D'},
{0, 0, 0, 0},
};
- c = getopt_long(argc, argv, "hz:y:x:d:s:r:t:", long_options,
+ c = getopt_long(argc, argv, "hz:y:x:d:s:r:t:T:D:", long_options,
&option_index);
if (c == -1)
break;
@@ -124,6 +133,12 @@ static void handle_options(int argc, char **argv)
case 't':
pm_timeout = optarg;
break;
+ case 'T':
+ mcast_ttl = atoi(optarg);
+ break;
+ case 'D':
+ mcast_netdev = optarg;
+ break;
default:
break;
}
@@ -227,12 +242,13 @@ 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");
- g_vphy.virt_um = virt_um_init(tall_vphy_ctx, ul_tx_grp, port, dl_rx_grp, port,
- gsmtapl1_rx_from_virt_um_inst_cb);
+ g_vphy.virt_um = virt_um_init(tall_vphy_ctx, ul_tx_grp, port, dl_rx_grp, port, mcast_ttl,
+ mcast_netdev, gsmtapl1_rx_from_virt_um_inst_cb);
g_vphy.l1ctl_sock = l1ctl_sock_init(tall_vphy_ctx, l1ctl_sap_rx_from_l23_inst_cb,
l1ctl_accept_cb, l1ctl_close_cb, l1ctl_sock_path);
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 f7235ac3..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,72 +36,97 @@ 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
-calypso_COMMON_OBJS=board/common/calypso_uart.o board/common/calypso_pwl.o
+calypso_COMMON_OBJS=board/common/calypso_uart.o \
+ board/common/calypso_pwl.o board/common/tx_calchan.o
# OpenMoko GTA0x
BOARD_gta0x_OBJS=$(calypso_COMMON_OBJS) board/gta0x/init.o \
- board/gta0x/rffe_gta0x_triband.o board/gta0x/rf_power.o \
- battery/dummy.o $(FB_dummy_OBJS)
+ board/gta0x/rffe_gta0x_triband.o board/gta0x/rf_tables.o \
+ board/gta0x/afcparams.o \
+ board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS)
BOARD_gta0x_ENVIRONMENTS=highram
# Huawei GTM900-B
BOARD_gtm900b_OBJS=$(calypso_COMMON_OBJS) board/gtm900b/init.o \
- board/gtm900b/rffe_gtm900b.o board/gtm900b/rf_power.o \
- battery/dummy.o $(FB_dummy_OBJS)
+ board/gtm900b/rffe_gtm900b.o board/gta0x/rf_tables.o \
+ board/gtm900b/afcparams.o \
+ board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS)
BOARD_gtm900b_ENVIRONMENTS=highram
# FreeCalypso FCDEV3B
BOARD_fcdev3b_OBJS=$(calypso_COMMON_OBJS) board/fcdev3b/init.o \
- board/gta0x/rffe_gta0x_triband.o board/gta0x/rf_power.o \
- battery/dummy.o $(FB_dummy_OBJS)
+ board/gta0x/rffe_gta0x_triband.o board/gta0x/rf_tables.o \
+ board/gta0x/afcparams.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 board/pirelli_dpl10/rf_power.o \
+ board/pirelli_dpl10/rffe_dpl10_triband.o \
+ board/pirelli_dpl10/rf_tables.o board/pirelli_dpl10/readcal.o \
battery/dummy.o $(FB_dpl10_OBJS)
BOARD_pirelli_dpl10_ENVIRONMENTS=highram
# Compal Generic
compal_COMMON_OBJS=$(calypso_COMMON_OBJS) \
- board/compal/rffe_dualband.o board/compal/rf_power.o
+ board/compal/rffe_dualband.o board/compal/rf_tables.o \
+ board/compal/readcal_common.o
compal_COMMON_ENVIRONMENTS=compalram highram
# Compal E88
BOARD_compal_e88_OBJS=$(compal_COMMON_OBJS) board/compal_e88/init.o \
+ board/compal/readcal_small.o board/compal_e88/tx_ramps.o \
battery/compal_e88.o $(FB_e88_OBJS)
BOARD_compal_e88_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS) e88loader e88flash
# Compal E86 (has a different RFFE configuration)
BOARD_compal_e86_OBJS=$(calypso_COMMON_OBJS) board/compal_e86/init.o \
- board/compal_e86/rffe_dualband_e86.o board/compal/rf_power.o \
- battery/dummy.o $(FB_e86_OBJS)
+ board/compal_e86/rffe_dualband_e86.o board/compal/rf_tables.o \
+ board/compal/readcal_common.o board/compal/readcal_small.o \
+ board/compal_e86/tx_ramps.o battery/dummy.o $(FB_e86_OBJS)
BOARD_compal_e86_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS)
# Compal E99
BOARD_compal_e99_OBJS=$(compal_COMMON_OBJS) board/compal_e99/init.o \
+ board/compal_e99/readcal.o board/compal_e88/tx_ramps.o \
battery/dummy.o $(FB_e99_OBJS)
BOARD_compal_e99_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS)
# Sony Ericsson J100 (made by Compal)
BOARD_se_j100_OBJS=$(compal_COMMON_OBJS) board/se_j100/init.o \
+ board/compal/readcal_small.o board/se_j100/tx_ramps.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
#
# List of all applications (meant to be overridden on command line)
-APPLICATIONS?=hello_world compal_dsp_dump layer1 loader rssi
+APPLICATIONS?=hello_world compal_dsp_dump layer1 loader rssi menu
# Applications specific env requirements
APP_loader_ENVIRONMENTS=compalram highram
APP_rssi_ENVIRONMENTS=* -compalram
+APP_menu_ENVIRONMENTS=* -highram
# Various objects that are currently linked into all applications
FLASH_OBJS=flash/cfi_flash.o
@@ -115,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 a91cf4f8..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 ####
@@ -39,6 +39,7 @@ Q_SIZE = $(if $(V:1=),@echo " SIZE $@";)
#### GIT VERSION ####
GIT_COMMIT:=$(shell git describe --always)
+GIT_SHORTHASH:=$(shell git rev-parse --short HEAD)
GIT_MODIFIED:=$(shell (git status | grep "modified:\|added:\|deleted:" -q) && echo "-modified")
GIT_REVISION:=$(GIT_COMMIT)$(GIT_MODIFIED)
@@ -46,6 +47,9 @@ GIT_REVISION:=$(GIT_COMMIT)$(GIT_MODIFIED)
ASFLAGS += -DGIT_REVISION=\"$(GIT_REVISION)\"
CFLAGS += -DGIT_REVISION=\"$(GIT_REVISION)\"
+ASFLAGS += -DGIT_SHORTHASH=\"$(GIT_SHORTHASH)\"
+CFLAGS += -DGIT_SHORTHASH=\"$(GIT_SHORTHASH)\"
+
#### GLOBAL DATA ####
ALL_OBJS=
@@ -95,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 ad2b01f9..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>
@@ -229,6 +225,15 @@ void twl3025_power_off(void)
twl3025_reg_write(VRPCDEV, 0x01);
}
+void twl3025_power_off_now(void)
+{
+ /* The phone will restart if the power butten has not been released.
+ * This can be useful for development. */
+ unsigned long flags;
+ local_firq_save(flags);
+ twl3025_reg_write(VRPCDEV, 0x01);
+}
+
void twl3025_clk13m(int enable)
{
if (enable) {
@@ -368,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 59ffe972..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>
@@ -33,6 +29,7 @@
#include <abb/twl3025.h>
#include <rf/trf6151.h>
+#include <rf/readcal.h>
#include <comm/sercomm.h>
#include <comm/timer.h>
@@ -60,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);
@@ -104,8 +100,9 @@ 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();
tpu_frame_irq_en(1, 1);
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
new file mode 100644
index 00000000..5d96cc0a
--- /dev/null
+++ b/src/target/firmware/apps/menu/main.c
@@ -0,0 +1,333 @@
+/* Menu for Calypso Phone to load applicatios from flash */
+
+/* (C) 2013 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.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <delay.h>
+#include <byteorder.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include <calypso/clock.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/irq.h>
+#include <calypso/misc.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+#include <uart.h>
+#include <fb/framebuffer.h>
+#include <battery/battery.h>
+#include <asm/system.h>
+
+#define RAM 0x00820000
+#define MAGIC 0x0083ff00
+
+static enum key_codes key_code = KEY_INV;
+static volatile enum key_states key_state;
+
+static int cursor = 0, scroll_apps = 0;
+
+static struct apps {
+ char name[16];
+ void *start;
+ int len;
+} apps[32];
+
+static void locate_apps(void)
+{
+ int i, j, k;
+ char *p;
+
+ memset(apps, 0, sizeof(apps));
+
+ for (j = 0, i = 0x010000; i < 0x200000; i += 0x10000) {
+ p = (char *)i;
+ /* check for highram header: "highram:" */
+ if (!!memcmp(p, "highram:", 8))
+ continue;
+ p += 8;
+ /* check for app name after header: "highram:<name>\n" */
+ printf("found highram image at flash mem address 0x%p\n",
+ (char *)i);
+ for (k = 0; k < (int)sizeof(apps[j].name) - 1; k++) {
+ if (p[k] == '\n')
+ break;
+ }
+ if (k == sizeof(apps[j].name) - 3) {
+ printf("skipping: corrupt highram header, no '\\n' "
+ "after image name or name more larger than %d "
+ "digits\n", (int)sizeof(apps[j].name) - 3);
+ continue;
+ }
+ if (j < 9)
+ apps[j].name[0] = '1' + j;
+ else if (j == 9)
+ apps[j].name[0] = '0';
+ else
+ apps[j].name[0] = ' ';
+ apps[j].name[1] = ' ';
+ memcpy(apps[j].name + 2, p, k);
+ apps[j].len = 0x20000;
+ p += k + 1;
+ /* p points to highram image after header */
+ apps[j].start = p;
+ j++;
+ }
+}
+
+static void wait_key_release(void)
+{
+ /* wait for key release */
+ while (key_state == PRESSED) {
+ delay_ms(10);
+ keypad_poll();
+ }
+}
+
+static void load_app(void)
+{
+ static int i;
+ static void (*f) (void) = (void (*)(void))RAM;
+
+ wait_key_release();
+
+ local_irq_disable();
+
+ for (i = 0; i < apps[cursor].len; i++)
+ ((unsigned char *)RAM)[i] = ((unsigned char *)apps[cursor].start)[i];
+ f();
+}
+
+/* UI */
+
+static void refresh_display(void)
+{
+#if 0
+ char text[16];
+ int bat = battery_info.battery_percent;
+#endif
+ int i;
+
+ fb_clear();
+
+ /* header */
+ fb_setbg(FB_COLOR_WHITE);
+ if (1) {
+ fb_setfg(FB_COLOR_BLUE);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(0, 7);
+ fb_putstr("Osmocom Menu", -1);
+ fb_setfg(FB_COLOR_RGB(0xc0, 0xc0, 0x00));
+ fb_setfont(FB_FONT_SYMBOLS);
+#if 0
+ fb_gotoxy(framebuffer->width - 15, 8);
+ if (bat >= 100 && (battery_info.flags & BATTERY_CHG_ENABLED)
+ && !(battery_info.flags & BATTERY_CHARGING))
+ fb_putstr("@HHBC", framebuffer->width);
+ else {
+ sprintf(text, "@%c%c%cC", (bat >= 30) ? 'B':'A',
+ (bat >= 60) ? 'B':'A', (bat >= 90) ? 'B':'A');
+ fb_putstr(text, framebuffer->width);
+ }
+#endif
+ fb_gotoxy(0, 8);
+ fb_putstr("GGEGG", framebuffer->width);
+ fb_setfg(FB_COLOR_GREEN);
+ fb_gotoxy(0, 10);
+ fb_boxto(framebuffer->width - 1, 10);
+ }
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setfont(FB_FONT_C64);
+
+
+ for (i = 0; i < 5; i++) {
+ if (!apps[scroll_apps + i].name)
+ break;
+ if (scroll_apps + i == cursor) {
+ fb_setfg(FB_COLOR_WHITE);
+ fb_setbg(FB_COLOR_BLUE);
+ }
+ fb_gotoxy(0, 20 + i * 10);
+ fb_putstr(apps[scroll_apps + i].name,
+ framebuffer->width);
+ if (scroll_apps + i == cursor) {
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ }
+ }
+ if (i == 0) {
+ fb_gotoxy(0, 50);
+ fb_putstr("No apps!", -1);
+ }
+
+ fb_flush();
+}
+
+static void handle_key_code()
+{
+ if (key_code == KEY_INV)
+ return;
+
+ switch (key_code) {
+ case KEY_1:
+ case KEY_2:
+ case KEY_3:
+ case KEY_4:
+ case KEY_5:
+ case KEY_6:
+ case KEY_7:
+ case KEY_8:
+ case KEY_9:
+ if (apps[key_code - KEY_1].len) {
+ cursor = key_code - KEY_1;
+ load_app();
+ }
+ break;
+ case KEY_0:
+ if (apps[9].len) {
+ cursor = 9;
+ load_app();
+ }
+ break;
+ case KEY_UP:
+ if (cursor == 0)
+ return;
+ cursor--;
+ if (cursor < scroll_apps)
+ scroll_apps = cursor;
+ refresh_display();
+ break;
+ case KEY_DOWN:
+ if (!apps[cursor + 1].name[0])
+ return;
+ cursor++;
+ if (cursor >= scroll_apps + 5)
+ scroll_apps = cursor - 4;
+ refresh_display();
+ break;
+ case KEY_OK:
+ if (apps[cursor].len)
+ load_app();
+ break;
+ case KEY_POWER:
+ wait_key_release();
+ twl3025_power_off();
+ break;
+ default:
+ break;
+ }
+
+ key_code = KEY_INV;
+}
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+static void key_handler(enum key_codes code, enum key_states state)
+{
+ key_state = state;
+
+ if (state != PRESSED)
+ return;
+
+ key_code = code;
+}
+
+extern void putchar_asm(uint32_t c);
+
+static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 };
+
+int main(void)
+{
+ int i;
+
+ /* Simulate a compal loader saying "ACK" */
+ for (i = 0; i < (int)sizeof(phone_ack); i++) {
+ putchar_asm(phone_ack[i]);
+ }
+
+ board_init(0);
+
+ puts("\n\nOsmocomBB Menu (revision " GIT_REVISION ")\n");
+ puts(hr);
+
+ fb_clear();
+
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ fb_setfont(FB_FONT_HELVB14);
+
+ fb_gotoxy(2,20);
+ fb_putstr("menu",framebuffer->width-4);
+
+ fb_setfg(FB_COLOR_RED);
+ fb_setbg(FB_COLOR_BLUE);
+
+ fb_gotoxy(2,25);
+ fb_boxto(framebuffer->width-3,38);
+
+ fb_setfg(FB_COLOR_WHITE);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_gotoxy(8,33);
+ fb_putstr("osmocom-bb",framebuffer->width-4);
+
+ fb_flush();
+
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ /* Dump clock config before PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ keypad_set_handler(&key_handler);
+
+ /* Dump clock config after PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+// display_unset_attr(DISP_ATTR_INVERT);
+
+ locate_apps();
+
+ while (1) {
+ for (i = 0; i < 50; i++) {
+ keypad_poll();
+ delay_ms(10);
+ osmo_timers_update();
+ handle_key_code();
+ }
+ refresh_display();
+ }
+
+ /* NOT REACHED */
+
+ twl3025_power_off();
+}
+
diff --git a/src/target/firmware/apps/rssi/main.c b/src/target/firmware/apps/rssi/main.c
index 50204869..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>
@@ -34,6 +30,7 @@
#include <board.h>
#include <abb/twl3025.h>
#include <rf/trf6151.h>
+#include <rf/readcal.h>
#include <calypso/clock.h>
#include <calypso/tpu.h>
#include <calypso/tsp.h>
@@ -170,7 +167,7 @@ static void print_display(char *text, int *y, int c)
static void refresh_display(void)
{
- char text[16];
+ char text[32];
int bat = battery_info.battery_percent;
fb_clear();
@@ -919,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;
@@ -1260,7 +1254,7 @@ static void handle_assign(void)
/* Main Program */
const char *hr = "======================================================================\n";
-/* match request reference agains request history */
+/* match request reference against request history */
static int gsm48_match_ra(struct gsm48_req_ref *ref)
{
uint8_t ia_t1, ia_t2, ia_t3;
@@ -1528,6 +1522,7 @@ int main(void)
sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb);
sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
+ read_factory_rf_calibration();
layer1_init();
l1a_l23_tx_cb = l1a_l23_tx;
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
new file mode 100644
index 00000000..4945b6f7
--- /dev/null
+++ b/src/target/firmware/apps/snake_game/main.c
@@ -0,0 +1,517 @@
+/* The game Snake as Free Software for Calypso Phone */
+
+/* (C) 2013 by Marcel `sdrfnord` McKinnon <sdrfnord@gmx.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 <string.h>
+
+#include <debug.h>
+#define DEBUG 1
+#define KNRM "\x1B[0m"
+#define UNDERLINE "\x1B[4m"
+
+#include <memory.h>
+#include <delay.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include <calypso/clock.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/irq.h>
+#include <calypso/misc.h>
+#include <calypso/backlight.h>
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+#include <fb/framebuffer.h>
+#include <battery/battery.h>
+
+unsigned long next = 1;
+/* This is not a good random number generator ... */
+int rand(void)
+{
+ next = next * 110351 + 12;
+ return (unsigned int)(next & 0x7fff);
+}
+
+void srand(unsigned int seed)
+{
+ next = seed;
+}
+
+#define BLANK 0
+#define HEAD 1
+#define TAIL 2
+#define HEAD_FOOD 3
+#define FOOD 9
+#define SBODY 20
+/* The numbers above 20 are the distance to the head.
+ * 21 is direly behind the head.
+ */
+#define STDLEN 3
+#define HEIGHT 7
+#define WIDTH 16
+
+/* Time in ms to wait to the next auto move of the snake. */
+#define WAIT_TIME_AUTOMOVE 300
+
+struct position {
+ int x;
+ int y;
+} pos;
+
+uint8_t field[WIDTH][HEIGHT];
+int16_t score = 0, lenght = 0;
+enum errors { ALLRIGHT, SNAKE_COL } err;
+
+void printField();
+void setItem(int, int, int);
+void movepos(char);
+void increaseBodyAge();
+void setFood()
+{
+ int x, y, c;
+ for (c = 0; c < 10; c++) {
+ x = rand() % (WIDTH - 1);
+ y = rand() % (HEIGHT - 1);
+#if DEBUG > 0
+ printf("Next %u\n", next);
+ printf("Rand (%d|%d)\n", x, y);
+#endif
+ if (field[x][y] == BLANK) {
+ field[x][y] = FOOD;
+ return;
+ }
+ }
+ for (x = 0; x < WIDTH; x++) {
+ for (y = 0; y < HEIGHT; y++) {
+ if (field[x][y] == BLANK) {
+ field[x][y] = FOOD;
+#if DEBUG > 0
+ printf("Set without rand (%d|%d) %d\n", x, y,
+ c);
+#endif
+ return;
+ }
+ }
+ }
+}
+
+static void print_snake_str(char *text, int16_t x, int16_t y)
+{
+ x = 6 * x;
+ y = 8 * (y + 1) - 3;
+#if DEBUG > 1
+ printf("Put string %s to (%d|%d)\n", text, x, y);
+#endif
+ fb_gotoxy(x, y);
+ fb_putstr(text, framebuffer->width);
+}
+
+char Move;
+void movepos(char move)
+{
+ Move = move;
+ setItem(pos.x, pos.y, SBODY);
+ switch (move) {
+ case 'h': pos.x--; break;
+ case 'j': pos.y++; break;
+ case 'k': pos.y--; break;
+ case 'l': pos.x++; break;
+ }
+ switch (move) {
+ case 'j':
+ case 'k':
+ if (pos.y == -1)
+ pos.y = HEIGHT - 1;
+ else if (pos.y == HEIGHT)
+ pos.y = 0;
+ increaseBodyAge();
+ break;
+ case 'l':
+ case 'h':
+ if (pos.x == -1)
+ pos.x = WIDTH - 1;
+ else if (pos.x == WIDTH)
+ pos.x = 0;
+ increaseBodyAge();
+ break;
+ }
+ setItem(pos.x, pos.y, HEAD);
+ printField();
+}
+
+void movepos_timer_cb(void *p)
+{
+ struct osmo_timer_list *tmr = (struct osmo_timer_list *)p;
+#if DEBUG > 0
+ printf("Auto move %c\n", Move);
+#endif
+ movepos(Move);
+
+ osmo_timer_schedule(tmr, WAIT_TIME_AUTOMOVE);
+}
+
+static struct osmo_timer_list move_snake_timer = {
+ .cb = &movepos_timer_cb,
+ .data = &move_snake_timer
+};
+
+void movepos_keypress(char keypress)
+{
+ Move = keypress;
+ osmo_timer_schedule(&move_snake_timer, 0);
+}
+
+void increaseBodyAge()
+{
+ int y, x;
+ lenght = SBODY + STDLEN + score;
+ for (x = 0; x < WIDTH; x++) {
+ for (y = 0; y < HEIGHT; y++) {
+ if (field[x][y] >= lenght)
+ field[x][y] = BLANK;
+ else if (field[x][y] >= SBODY)
+ field[x][y]++;
+ }
+ }
+}
+
+void setItem(int x, int y, int item)
+{
+ if (item == HEAD) {
+ switch (field[x][y]) {
+ case FOOD:
+ score++;
+ setFood();
+ item = HEAD_FOOD;
+ break;
+ case BLANK:
+ break;
+ default:
+ err = SNAKE_COL;
+ score--;
+ }
+ }
+ field[x][y] = item;
+}
+
+void resetField()
+{
+ /* system("clear"); */
+ printf("\033[H\033[2J");
+}
+
+void printField()
+{
+ fb_clear();
+ int x, y;
+ for (y = 0; y < HEIGHT; y++) {
+ for (x = 0; x < WIDTH; x++) {
+ switch (field[x][y]) {
+ case BLANK:
+ break;
+ case HEAD:
+ print_snake_str("O", x, y);
+ break;
+ case HEAD_FOOD:
+ print_snake_str("P", x, y);
+ break;
+ case FOOD:
+ print_snake_str("#", x, y);
+ break;
+ default:
+ if (field[x][y] == lenght)
+ print_snake_str(";", x, y);
+ else
+ print_snake_str("o", x, y);
+ }
+ }
+
+ }
+ printf("Score: %d\n", score);
+ fb_gotoxy(0, framebuffer->height - 9);
+ fb_lineto(framebuffer->width - 1, framebuffer->cursor_y);
+ fb_gotoxy(0, framebuffer->height - 1);
+ char text[16];
+ switch (err) {
+ case SNAKE_COL:
+ fb_putstr("The snake ate itself!!!", framebuffer->width);
+ err = ALLRIGHT;
+ break;
+ default:
+ sprintf(text, "Score: %d", score);
+ fb_putstr(text, framebuffer->width);
+ framebuffer->cursor_x = 45;
+ fb_putstr("OsmocomBB", framebuffer->width);
+ }
+ fb_flush();
+
+#if DEBUG > 0
+ printf("Pos X: %d, Y: %d\n", pos.x, pos.y);
+ printf("\n\n");
+ for (y = -1; y < HEIGHT; y++) {
+ for (x = -1; x < WIDTH; x++) {
+ if (y == -1 || x == -1) {
+ if (x == -1)
+ printf(" %2d: ", y);
+ else if (y == -1)
+ printf(UNDERLINE " %2d" KNRM,
+ x);
+ } else
+ printf(" %2d", field[x][y]);
+ }
+ puts("\n");
+ }
+#endif
+}
+
+int cursor = 0;
+#define NEIGH_LINES ((framebuffer->height +8) / 8)
+static void print_display(char *text, int *y, int c)
+{
+ /* skip lines, given by cursor */
+ (*y)++;
+ if (c >= (*y)) {
+ printf("Line %d: Return c >= y\n", *y);
+ return;
+ }
+ /* skip, if end of display area is reached */
+ if ((*y) - c > NEIGH_LINES) {
+ printf("Line %d: Return\n", *y);
+ return;
+ }
+
+ fb_gotoxy(0, -3 + (((*y) - c - 1) << 3));
+ fb_putstr(text, framebuffer->width);
+}
+
+void fb_clear_fancy(uint8_t delay)
+{
+ int16_t x, y;
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ for (x = 0; x < framebuffer->width; x++) {
+ for (y = 0; y < framebuffer->height; y++) {
+ fb_set_p(x, y);
+ }
+ fb_flush();
+ delay_ms(delay);
+ }
+ fb_setfg(FB_COLOR_WHITE);
+ fb_setbg(FB_COLOR_BLACK);
+ /* for (x = 0; x < framebuffer->width; x++) { */
+ for (; x >= 0; x--) {
+ for (y = 0; y < framebuffer->height; y++) {
+ fb_set_p(x, y);
+ }
+ fb_flush();
+ delay_ms(delay);
+ }
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+}
+
+void intro()
+{
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ fb_setfont(FB_FONT_HELVB14);
+
+ fb_gotoxy(framebuffer->width / 2 - 7 * 3, 15);
+ fb_putstr("Snake", framebuffer->width - 4);
+
+ fb_gotoxy(14, framebuffer->height - 5);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_putstr("Version: " GIT_SHORTHASH, framebuffer->width - 4);
+ fb_gotoxy(0, 0);
+ fb_boxto(framebuffer->width - 1, 1);
+ fb_boxto(framebuffer->width - 2, framebuffer->height - 1);
+ fb_boxto(0, framebuffer->height - 2);
+ fb_boxto(1, 1);
+
+ printf("(%u, %u)\n", framebuffer->width, framebuffer->height);
+ fb_gotoxy(2, 2);
+ fb_lineto(framebuffer->width - 3, framebuffer->height - 3);
+ fb_gotoxy(2, framebuffer->height - 3);
+ fb_lineto(framebuffer->width - 3, 2);
+ fb_flush();
+}
+
+/* Main Program */
+const char *hr =
+ "======================================================================\n";
+
+void key_handler(enum key_codes code, enum key_states state);
+
+static void console_rx_cb(uint8_t dlci, struct msgb *msg)
+{
+ if (dlci != SC_DLCI_CONSOLE) {
+ printf("Message for unknown DLCI %u\n", dlci);
+ return;
+ }
+
+ printf("Message on console DLCI: '%s'\n", msg->data);
+ msgb_free(msg);
+}
+
+static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg)
+{
+ int i;
+ puts("l1a_l23_rx_cb: ");
+ for (i = 0; i < msg->len; i++)
+ printf("%02x ", msg->data[i]);
+ puts("\n");
+}
+
+int main(void)
+{
+ board_init(1);
+
+ puts("\n\nOsmocomBB Test sdrfnord (revision " GIT_REVISION ")\n");
+ puts(hr);
+
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ /* Dump clock config before PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ /* Dump clock config after PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ fb_clear();
+ bl_level(255);
+ osmo_timers_update();
+
+ intro();
+ delay_ms(5000);
+ fb_clear_fancy(20);
+
+ fb_setfg(FB_COLOR_BLACK);
+ fb_setbg(FB_COLOR_WHITE);
+ fb_setfont(FB_FONT_HELVR08);
+ fb_flush();
+
+ pos.x = framebuffer->width / (6 * 2);
+ pos.y = framebuffer->height / (8 * 2);
+ setItem(pos.x, pos.y, HEAD);
+
+ while (battery_info.bat_volt_mV == 0)
+ osmo_timers_update();
+ srand(battery_info.bat_volt_mV);
+#if DEBUG > 0
+ printf("Initialize random number generator with %d\n",
+ battery_info.bat_volt_mV);
+#endif
+ setFood();
+ printField();
+
+ sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb);
+ sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
+ keypad_set_handler(&key_handler);
+
+ /* beyond this point we only react to interrupts */
+ puts("entering interrupt loop\n");
+ while (1) {
+ osmo_timers_update();
+ }
+
+ twl3025_power_off();
+
+ while (1) {
+ }
+}
+
+void key_handler(enum key_codes code, enum key_states state)
+{
+ if (!osmo_timer_pending(&move_snake_timer)) {
+ osmo_timer_schedule(&move_snake_timer, WAIT_TIME_AUTOMOVE);
+ }
+ if (state != PRESSED)
+ return;
+
+ switch (code) {
+ case KEY_0:
+ bl_level(0);
+ break;
+ case KEY_1:
+ bl_level(10);
+ break;
+ case KEY_2:
+ movepos_keypress('k');
+ break;
+ case KEY_3:
+ bl_level(30);
+ break;
+ case KEY_4:
+ movepos_keypress('h');
+ break;
+ case KEY_5:
+ bl_level(50);
+ break;
+ case KEY_6:
+ movepos_keypress('l');
+ break;
+ case KEY_7:
+ bl_level(150);
+ break;
+ case KEY_8:
+ movepos_keypress('j');
+ break;
+ case KEY_9:
+ bl_level(255);
+ break;
+ // used to be display_puts...
+ break;
+ case KEY_STAR:
+ // used to be display puts...
+ break;
+ case KEY_HASH:
+ // used to be display puts...
+ break;
+ case KEY_LEFT_SB:
+ bl_mode_pwl(1);
+ break;
+ case KEY_RIGHT_SB:
+ bl_mode_pwl(0);
+ break;
+ case KEY_POWER:
+ twl3025_power_off_now();
+ break;
+ case KEY_RIGHT:
+ movepos_keypress('l');
+ break;
+ case KEY_LEFT:
+ movepos_keypress('h');
+ break;
+ case KEY_UP:
+ movepos_keypress('k');
+ break;
+ case KEY_DOWN:
+ movepos_keypress('j');
+ break;
+ default:
+ break;
+ }
+}
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
new file mode 100644
index 00000000..175f25a7
--- /dev/null
+++ b/src/target/firmware/board/common/readcal_tiffs.c
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 <rf/readcal.h>
+#include <rf/vcxocal.h>
+#include <rf/txcal.h>
+#include <tiffs.h>
+
+static int16_t afcdac_shifted;
+
+static void afcdac_postproc(void)
+{
+ afc_initial_dac_value = afcdac_shifted >> 3;
+}
+
+static const struct calmap {
+ char *pathname;
+ size_t record_len;
+ void *buffer;
+ void (*postproc)(void);
+} rf_cal_list[] = {
+ { "/gsm/rf/afcdac", 2, &afcdac_shifted, afcdac_postproc },
+ { "/gsm/rf/tx/ramps.850", 512, rf_tx_ramps_850, NULL },
+ { "/gsm/rf/tx/levels.850", 128, rf_tx_levels_850, NULL },
+ { "/gsm/rf/tx/calchan.850", 128, rf_tx_chan_cal_850, NULL },
+ { "/gsm/rf/tx/ramps.900", 512, rf_tx_ramps_900, NULL },
+ { "/gsm/rf/tx/levels.900", 128, rf_tx_levels_900, NULL },
+ { "/gsm/rf/tx/calchan.900", 128, rf_tx_chan_cal_900, NULL },
+ { "/gsm/rf/tx/ramps.1800", 512, rf_tx_ramps_1800, NULL },
+ { "/gsm/rf/tx/levels.1800", 128, rf_tx_levels_1800, NULL },
+ { "/gsm/rf/tx/calchan.1800", 128, rf_tx_chan_cal_1800, NULL },
+ { "/gsm/rf/tx/ramps.1900", 512, rf_tx_ramps_1900, NULL },
+ { "/gsm/rf/tx/levels.1900", 128, rf_tx_levels_1900, NULL },
+ { "/gsm/rf/tx/calchan.1900", 128, rf_tx_chan_cal_1900, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+void read_factory_rf_calibration(void)
+{
+ const struct calmap *tp;
+ uint8_t buf[512];
+ int rc;
+
+ puts("Checking TIFFS for the RF calibration records\n");
+ for (tp = rf_cal_list; tp->pathname; tp++) {
+ rc = tiffs_read_file_fixedlen(tp->pathname, buf,
+ tp->record_len);
+ if (rc <= 0)
+ continue;
+ printf("Found '%s', applying\n", tp->pathname);
+ memcpy(tp->buffer, buf, tp->record_len);
+ if (tp->postproc)
+ tp->postproc();
+ }
+}
diff --git a/src/target/firmware/board/common/tx_calchan.c b/src/target/firmware/board/common/tx_calchan.c
new file mode 100644
index 00000000..b86464d6
--- /dev/null
+++ b/src/target/firmware/board/common/tx_calchan.c
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+
+/*
+ * On the better FreeCalypso, Openmoko and Pirelli targets, the following
+ * Tx channel correction tables are placeholders to be overridden by
+ * per-unit calibration, but Compal did their Tx channel calibration
+ * in some different way which we haven't been able to grok, hence on
+ * these targets we currently always run with these dummy tables,
+ * and no channel-dependent corrections are applied.
+ */
+
+struct txcal_chan_cal rf_tx_chan_cal_850[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS] = {
+ {
+ { 134, 128 },
+ { 150, 128 },
+ { 166, 128 },
+ { 182, 128 },
+ { 197, 128 },
+ { 213, 128 },
+ { 229, 128 },
+ { 251, 128 },
+ },
+ {
+ { 134, 128 },
+ { 150, 128 },
+ { 166, 128 },
+ { 182, 128 },
+ { 197, 128 },
+ { 213, 128 },
+ { 229, 128 },
+ { 251, 128 },
+ },
+ {
+ { 134, 128 },
+ { 150, 128 },
+ { 166, 128 },
+ { 182, 128 },
+ { 197, 128 },
+ { 213, 128 },
+ { 229, 128 },
+ { 251, 128 },
+ },
+ {
+ { 134, 128 },
+ { 150, 128 },
+ { 166, 128 },
+ { 182, 128 },
+ { 197, 128 },
+ { 213, 128 },
+ { 229, 128 },
+ { 251, 128 },
+ },
+};
+
+struct txcal_chan_cal rf_tx_chan_cal_900[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS] = {
+ {
+ { 27, 128 },
+ { 47, 128 },
+ { 66, 128 },
+ { 85, 128 },
+ { 104, 128 },
+ { 124, 128 },
+ { 994, 128 },
+ { 1023, 128 },
+ },
+ {
+ { 27, 128 },
+ { 47, 128 },
+ { 66, 128 },
+ { 85, 128 },
+ { 104, 128 },
+ { 124, 128 },
+ { 994, 128 },
+ { 1023, 128 },
+ },
+ {
+ { 27, 128 },
+ { 47, 128 },
+ { 66, 128 },
+ { 85, 128 },
+ { 104, 128 },
+ { 124, 128 },
+ { 994, 128 },
+ { 1023, 128 },
+ },
+ {
+ { 27, 128 },
+ { 47, 128 },
+ { 66, 128 },
+ { 85, 128 },
+ { 104, 128 },
+ { 124, 128 },
+ { 994, 128 },
+ { 1023, 128 },
+ },
+};
+
+struct txcal_chan_cal rf_tx_chan_cal_1800[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS] = {
+ {
+ { 553, 128 },
+ { 594, 128 },
+ { 636, 128 },
+ { 677, 128 },
+ { 720, 128 },
+ { 760, 128 },
+ { 802, 128 },
+ { 885, 128 },
+ },
+ {
+ { 553, 128 },
+ { 594, 128 },
+ { 636, 128 },
+ { 677, 128 },
+ { 720, 128 },
+ { 760, 128 },
+ { 802, 128 },
+ { 885, 128 },
+ },
+ {
+ { 553, 128 },
+ { 594, 128 },
+ { 636, 128 },
+ { 677, 128 },
+ { 720, 128 },
+ { 760, 128 },
+ { 802, 128 },
+ { 885, 128 },
+ },
+ {
+ { 553, 128 },
+ { 594, 128 },
+ { 636, 128 },
+ { 677, 128 },
+ { 720, 128 },
+ { 760, 128 },
+ { 802, 128 },
+ { 885, 128 },
+ },
+};
+
+struct txcal_chan_cal rf_tx_chan_cal_1900[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS] = {
+ {
+ { 549, 128 },
+ { 586, 128 },
+ { 623, 128 },
+ { 697, 128 },
+ { 726, 128 },
+ { 754, 128 },
+ { 782, 128 },
+ { 810, 128 },
+ },
+ {
+ { 549, 128 },
+ { 586, 128 },
+ { 623, 128 },
+ { 697, 128 },
+ { 726, 128 },
+ { 754, 128 },
+ { 782, 128 },
+ { 810, 128 },
+ },
+ {
+ { 549, 128 },
+ { 586, 128 },
+ { 623, 128 },
+ { 697, 128 },
+ { 726, 128 },
+ { 754, 128 },
+ { 782, 128 },
+ { 810, 128 },
+ },
+ {
+ { 549, 128 },
+ { 586, 128 },
+ { 623, 128 },
+ { 697, 128 },
+ { 726, 128 },
+ { 754, 128 },
+ { 782, 128 },
+ { 810, 128 },
+ },
+};
diff --git a/src/target/firmware/board/compal/highram.lds b/src/target/firmware/board/compal/highram.lds
index 9309d83f..f2e04a36 100644
--- a/src/target/firmware/board/compal/highram.lds
+++ b/src/target/firmware/board/compal/highram.lds
@@ -16,7 +16,7 @@ MEMORY
XRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00020000
/* highram binary single big zone with all rest of internal SRAM */
/* -> our text, initialized data */
- /* -> our unitialized data, stacks, heap */
+ /* -> our uninitialized data, stacks, heap */
RAM (rw) : ORIGIN = 0x00820000, LENGTH = 0x00030000
}
SECTIONS
diff --git a/src/target/firmware/board/compal/ram.lds b/src/target/firmware/board/compal/ram.lds
index 4f08a835..ed7bb625 100644
--- a/src/target/firmware/board/compal/ram.lds
+++ b/src/target/firmware/board/compal/ram.lds
@@ -12,7 +12,7 @@ MEMORY
/* compal-loaded binary: our text, initialized data */
/* (only this zone can contain loaded data since loader is 64k limit) */
LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000
- /* compal-loaded binary: our unitialized data, stacks, heap */
+ /* compal-loaded binary: our uninitialized data, stacks, heap */
IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00030000
}
SECTIONS
diff --git a/src/target/firmware/board/compal/readcal_common.c b/src/target/firmware/board/compal/readcal_common.c
new file mode 100644
index 00000000..c0260b67
--- /dev/null
+++ b/src/target/firmware/board/compal/readcal_common.c
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 <inttypes.h>
+
+#include <rf/txcal.h>
+
+struct record_hdr {
+ uint16_t valid_flag;
+ uint16_t type;
+ uint16_t native_len;
+ uint16_t rounded_len;
+};
+
+static void apply_levels(struct txcal_tx_level *levels_table,
+ uint16_t *compal_data, unsigned start_level,
+ unsigned num_levels)
+{
+ unsigned n;
+
+ for (n = 0; n < num_levels; n++)
+ levels_table[start_level + n].apc = *compal_data++;
+}
+
+void read_compal_factory_records(uint32_t flash_addr)
+{
+ struct record_hdr *hdr;
+ void *p, *sector_end;
+ unsigned p_incr;
+ void *payload;
+
+ printf("Analyzing factory records sector "
+ "at 0x%" PRIx32 "\n", flash_addr);
+
+ p = (void *) flash_addr;
+ sector_end = p + 0x2000;
+ for (; p < sector_end; p += p_incr) {
+ if ((sector_end - p) < 12)
+ break;
+ hdr = (struct record_hdr *)p;
+ if (hdr->valid_flag == 0xFFFF) /* blank flash */
+ break;
+ if (hdr->native_len > hdr->rounded_len) {
+ printf("Bad record at 0x%" PRIx32 ": native length "
+ "> rounded length\n", (uint32_t) p);
+ return;
+ }
+ if (hdr->rounded_len & 3) {
+ printf("Bad record at 0x%" PRIx32 ": rounded length "
+ "is not aligned to 4\n", (uint32_t) p);
+ return;
+ }
+ p_incr = hdr->rounded_len + 8;
+ if (p_incr > (sector_end - p)) {
+ printf("Bad record at 0x%" PRIx32 ": rounded length "
+ "spills past the end of the sector\n", (uint32_t) p);
+ return;
+ }
+ if (hdr->valid_flag != 0x000C)
+ continue;
+ payload = (void *)(hdr + 1);
+ switch (hdr->type) {
+ case 0x0000:
+ if (hdr->native_len != 0x94)
+ break;
+ if (*(uint32_t *)(payload + 0x5C) != 0xAA)
+ break;
+ printf("Found 900 MHz band calibration record at "
+ "0x%" PRIx32 ", applying\n", (uint32_t) p);
+ apply_levels(rf_tx_levels_900, payload + 0x60, 5, 15);
+ break;
+ case 0x0001:
+ if (hdr->native_len != 0xC8)
+ break;
+ if (*(uint32_t *)(payload + 0x7C) != 0xAA)
+ break;
+ printf("Found 1800 MHz band calibration record at "
+ "0x%" PRIx32 ", applying\n", (uint32_t) p);
+ apply_levels(rf_tx_levels_1800, payload + 0x80, 0, 16);
+ break;
+ case 0x0002:
+ if (hdr->native_len != 0xB4)
+ break;
+ if (*(uint32_t *)(payload + 0x70) != 0xAA)
+ break;
+ printf("Found 1900 MHz band calibration record at "
+ "0x%" PRIx32 ", applying\n", (uint32_t) p);
+ apply_levels(rf_tx_levels_1900, payload + 0x74, 0, 16);
+ break;
+ case 0x0018:
+ if (hdr->native_len != 0x88)
+ break;
+ if (*(uint32_t *)(payload + 0x54) != 0xAA)
+ break;
+ printf("Found 850 MHz band calibration record at "
+ "0x%" PRIx32 ", applying\n", (uint32_t) p);
+ apply_levels(rf_tx_levels_850, payload + 0x58, 5, 15);
+ break;
+ }
+ }
+}
diff --git a/src/target/firmware/board/compal/readcal_small.c b/src/target/firmware/board/compal/readcal_small.c
new file mode 100644
index 00000000..32302d20
--- /dev/null
+++ b/src/target/firmware/board/compal/readcal_small.c
@@ -0,0 +1,26 @@
+/*
+ * 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/readcal.h>
+
+extern void read_compal_factory_records(uint32_t flash_addr);
+
+void read_factory_rf_calibration(void)
+{
+ read_compal_factory_records(0x3FC000);
+}
diff --git a/src/target/firmware/board/compal/rf_power.c b/src/target/firmware/board/compal/rf_power.c
deleted file mode 100644
index fbbe65a3..00000000
--- a/src/target/firmware/board/compal/rf_power.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Tx RF power calibration for the Compal/Motorola dualband phones */
-
-/* (C) 2010 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 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 <stdint.h>
-#include <osmocom/core/utils.h>
-
-/* GSM900 ARFCN 33, Measurements by Steve Markgraf / May 2010 */
-const int16_t dbm2apc_gsm900[] = {
- [0] = 151,
- [1] = 152,
- [2] = 153,
- [3] = 155,
- [4] = 156,
- [5] = 158,
- [6] = 160,
- [7] = 162,
- [8] = 164,
- [9] = 167,
- [10] = 170,
- [11] = 173,
- [12] = 177,
- [13] = 182,
- [14] = 187,
- [15] = 192,
- [16] = 199,
- [17] = 206,
- [18] = 214,
- [19] = 223,
- [20] = 233,
- [21] = 244,
- [22] = 260,
- [23] = 271,
- [24] = 288,
- [25] = 307,
- [26] = 327,
- [27] = 350,
- [28] = 376,
- [29] = 407,
- [30] = 456,
- [31] = 575,
-};
-
-const int dbm2apc_gsm900_max = ARRAY_SIZE(dbm2apc_gsm900) - 1;
diff --git a/src/target/firmware/board/compal/rf_tables.c b/src/target/firmware/board/compal/rf_tables.c
new file mode 100644
index 00000000..f115f29a
--- /dev/null
+++ b/src/target/firmware/board/compal/rf_tables.c
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+#include <rf/vcxocal.h>
+
+/*
+ * The following AFC initial DAC value and AFC slope settings are unchanged
+ * from the old OsmocomBB code in which they were hard-coded in layer1/afc.c.
+ * This AFC slope setting corresponds very closely to the original Leonardo
+ * Psi values which are used by Motorola's official fw at least on the C139,
+ * hence I have good reason to believe that they are indeed correct for the
+ * Mot C1xx hardware target family.
+ */
+int16_t afc_initial_dac_value = -700;
+int16_t afc_slope = 287;
+
+/*
+ * The following Tx levels tables are the ones compiled into Compal's
+ * firmwares; more specifically, they were originally extracted out
+ * of the one special Mot C11x fw version for which we got the linker
+ * map file with symbols and subsequently confirmed to be unchanged in
+ * Mot C139 and SE J100 firmwares. In normal operation the APC DAC values
+ * in these levels tables are replaced with the ones read from the per-unit
+ * and per-band factory calibration records.
+ *
+ * It should be noted that these compiled-in numbers are approximately
+ * correct for the C11x/12x/155/156 family (SKY77324 RF PA) but are totally
+ * wrong for the newer C139/140 (SKY77325) and SE J100 (SKY77328) hardware;
+ * it appears that Compal never bothered with changing these compiled-in
+ * numbers in their fw for the newer designs because the expectation is
+ * that these compiled-in numbers are just dummy placeholders to be
+ * overridden by per-unit calibration.
+ */
+struct txcal_tx_level rf_tx_levels_850[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 560, 0, 0 }, /* 0 */
+ { 560, 0, 0 }, /* 1 */
+ { 560, 0, 0 }, /* 2 */
+ { 560, 0, 0 }, /* 3 */
+ { 560, 0, 0 }, /* 4 */
+ { 638, 0, 0 }, /* 5 */
+ { 554, 1, 0 }, /* 6 */
+ { 467, 2, 0 }, /* 7 */
+ { 395, 3, 0 }, /* 8 */
+ { 337, 4, 0 }, /* 9 */
+ { 290, 5, 0 }, /* 10 */
+ { 253, 6, 0 }, /* 11 */
+ { 224, 7, 0 }, /* 12 */
+ { 201, 8, 0 }, /* 13 */
+ { 183, 9, 0 }, /* 14 */
+ { 168, 10, 0 }, /* 15 */
+ { 157, 11, 0 }, /* 16 */
+ { 148, 12, 0 }, /* 17 */
+ { 141, 13, 0 }, /* 18 */
+ { 136, 14, 0 }, /* 19 */
+ { 46, 14, 0 }, /* 20 */
+ { 46, 14, 0 }, /* 21 */
+ { 46, 14, 0 }, /* 22 */
+ { 46, 14, 0 }, /* 23 */
+ { 46, 14, 0 }, /* 24 */
+ { 46, 14, 0 }, /* 25 */
+ { 46, 14, 0 }, /* 26 */
+ { 46, 14, 0 }, /* 27 */
+ { 46, 14, 0 }, /* 28 */
+ { 46, 14, 0 }, /* 29 */
+ { 46, 14, 0 }, /* 30 */
+ { 46, 14, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_900[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 550, 0, 0 }, /* 0 */
+ { 550, 0, 0 }, /* 1 */
+ { 550, 0, 0 }, /* 2 */
+ { 550, 0, 0 }, /* 3 */
+ { 550, 0, 0 }, /* 4 */
+ { 550, 0, 0 }, /* 5 */
+ { 476, 1, 0 }, /* 6 */
+ { 402, 2, 0 }, /* 7 */
+ { 338, 3, 0 }, /* 8 */
+ { 294, 4, 0 }, /* 9 */
+ { 260, 5, 0 }, /* 10 */
+ { 226, 6, 0 }, /* 11 */
+ { 204, 7, 0 }, /* 12 */
+ { 186, 8, 0 }, /* 13 */
+ { 172, 9, 0 }, /* 14 */
+ { 161, 10, 0 }, /* 15 */
+ { 153, 11, 0 }, /* 16 */
+ { 146, 12, 0 }, /* 17 */
+ { 141, 13, 0 }, /* 18 */
+ { 137, 14, 0 }, /* 19 */
+ { 43, 14, 0 }, /* 20 */
+ { 43, 14, 0 }, /* 21 */
+ { 43, 14, 0 }, /* 22 */
+ { 43, 14, 0 }, /* 23 */
+ { 43, 14, 0 }, /* 24 */
+ { 43, 14, 0 }, /* 25 */
+ { 43, 14, 0 }, /* 26 */
+ { 43, 14, 0 }, /* 27 */
+ { 43, 14, 0 }, /* 28 */
+ { 43, 14, 0 }, /* 29 */
+ { 43, 14, 0 }, /* 30 */
+ { 43, 14, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_1800[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 480, 0, 0 }, /* 0 */
+ { 416, 1, 0 }, /* 1 */
+ { 352, 2, 0 }, /* 2 */
+ { 308, 3, 0 }, /* 3 */
+ { 266, 4, 0 }, /* 4 */
+ { 242, 5, 0 }, /* 5 */
+ { 218, 6, 0 }, /* 6 */
+ { 200, 7, 0 }, /* 7 */
+ { 186, 8, 0 }, /* 8 */
+ { 175, 9, 0 }, /* 9 */
+ { 167, 10, 0 }, /* 10 */
+ { 160, 11, 0 }, /* 11 */
+ { 156, 12, 0 }, /* 12 */
+ { 152, 13, 0 }, /* 13 */
+ { 145, 14, 0 }, /* 14 */
+ { 142, 15, 0 }, /* 15 */
+ { 61, 15, 0 }, /* 16 */
+ { 61, 15, 0 }, /* 17 */
+ { 61, 15, 0 }, /* 18 */
+ { 61, 15, 0 }, /* 19 */
+ { 61, 15, 0 }, /* 20 */
+ { 61, 15, 0 }, /* 21 */
+ { 61, 15, 0 }, /* 22 */
+ { 61, 15, 0 }, /* 23 */
+ { 61, 15, 0 }, /* 24 */
+ { 61, 15, 0 }, /* 25 */
+ { 61, 15, 0 }, /* 26 */
+ { 61, 15, 0 }, /* 27 */
+ { 61, 15, 0 }, /* 28 */
+ { 750, 0, 0 }, /* 29 */
+ { 750, 0, 0 }, /* 30 */
+ { 750, 0, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_1900[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 520, 0, 0 }, /* 0 */
+ { 465, 1, 0 }, /* 1 */
+ { 390, 2, 0 }, /* 2 */
+ { 330, 3, 0 }, /* 3 */
+ { 285, 4, 0 }, /* 4 */
+ { 250, 5, 0 }, /* 5 */
+ { 225, 6, 0 }, /* 6 */
+ { 205, 7, 0 }, /* 7 */
+ { 190, 8, 0 }, /* 8 */
+ { 177, 9, 0 }, /* 9 */
+ { 168, 10, 0 }, /* 10 */
+ { 161, 11, 0 }, /* 11 */
+ { 155, 12, 0 }, /* 12 */
+ { 150, 13, 0 }, /* 13 */
+ { 147, 14, 0 }, /* 14 */
+ { 143, 15, 0 }, /* 15 */
+ { 62, 15, 0 }, /* 16 */
+ { 62, 15, 0 }, /* 17 */
+ { 62, 15, 0 }, /* 18 */
+ { 62, 15, 0 }, /* 19 */
+ { 62, 15, 0 }, /* 20 */
+ { 62, 15, 0 }, /* 21 */
+ { 62, 15, 0 }, /* 22 */
+ { 62, 15, 0 }, /* 23 */
+ { 62, 15, 0 }, /* 24 */
+ { 62, 15, 0 }, /* 25 */
+ { 62, 15, 0 }, /* 26 */
+ { 62, 15, 0 }, /* 27 */
+ { 62, 15, 0 }, /* 28 */
+ { 915, 0, 0 }, /* 29 */
+ { 915, 0, 0 }, /* 30 */
+ { 915, 0, 0 }, /* 31 */
+};
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
new file mode 100644
index 00000000..195242a1
--- /dev/null
+++ b/src/target/firmware/board/compal_e86/tx_ramps.c
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+
+/*
+ * Mot C139/140 uses the same APC offset value as C11x, as verified by
+ * breaking into a running C139 fw with tfc139, running fc-loadtool with
+ * all ABB register state still intact from the interrupted official fw,
+ * and reading the APCOFF register with the abbr command.
+ */
+uint8_t apc_offset = 32;
+
+/*
+ * The following tables of Tx ramp templates have been read out of an
+ * official Mot C139 fw version via tms 1, rfpw 7 and ttr Test Mode commands.
+ * Please note that these Tx ramp templates for the SKY77325 PA in Mot C139/140
+ * phones are different from the older C11x/12x/155/156 (SKY77324) ones!
+ */
+struct txcal_ramp_def rf_tx_ramps_850[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 3, 0, 0, 0, 0, 0, 2, 31, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 20, 21, 31, 26, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 5, 0, 0, 0, 0, 0, 0, 31, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 5, 0, 0, 0, 0, 0, 0, 31, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 5, 0, 0, 0, 0, 0, 31, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 5, 0, 0, 0, 0, 0, 31, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 8, 0, 0, 0, 0, 0, 28, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 8, 0, 0, 0, 0, 0, 28, 30, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 12, 0, 0, 0, 0, 0, 20, 30, 31, 31, 4, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 12, 0, 0, 0, 0, 0, 20, 30, 31, 31, 4, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 17, 0, 0, 0, 0, 0, 25, 24, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 17, 0, 0, 0, 0, 0, 25, 24, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 17, 0, 0, 0, 0, 0, 14, 30, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 17, 0, 0, 0, 0, 0, 14, 30, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 2, 0, 0, 0, 0, 0, 13, 24, 24, 25, 26, 14, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 3, 0, 0, 0, 0, 0, 16, 24, 24, 30, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 3, 0, 0, 0, 0, 0, 16, 24, 24, 30, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 4, 0, 0, 0, 0, 0, 31, 31, 30, 30, 2, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 4, 0, 0, 0, 0, 0, 31, 31, 30, 30, 2, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 31, 31, 31, 20, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 31, 31, 31, 20, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 31, 31, 31, 20, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 31, 31, 31, 20, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 31, 31, 31, 17, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1800[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 6, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 30, 30, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 6, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 30, 30, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 6, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 30, 30, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 6, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 2, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 6, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 2, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 18, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 18, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 10, 0, 0, 0, 0, 0, 0, 0, 6, 15, 31, 31, 23, 12, 0, 0},
+ /* ramp-down */
+ { 31, 31, 27, 27, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 10, 0, 0, 0, 0, 0, 0, 0, 6, 15, 31, 31, 23, 12, 0, 0},
+ /* ramp-down */
+ { 31, 31, 27, 27, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 10, 0, 0, 0, 0, 0, 0, 0, 6, 15, 31, 31, 23, 12, 0, 0},
+ /* ramp-down */
+ { 31, 31, 27, 27, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 10, 0, 0, 0, 0, 0, 0, 0, 6, 15, 31, 31, 23, 12, 0, 0},
+ /* ramp-down */
+ { 31, 31, 27, 27, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 10, 0, 0, 0, 0, 0, 0, 0, 6, 15, 31, 31, 23, 12, 0, 0},
+ /* ramp-down */
+ { 31, 31, 27, 27, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 25, 26, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 16, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 16, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 19, 0, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 23, 0, 0, 0, 0, 0, 0, 0, 0, 25, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 23, 0, 0, 0, 0, 0, 0, 0, 0, 25, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
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
new file mode 100644
index 00000000..79b3180d
--- /dev/null
+++ b/src/target/firmware/board/compal_e88/tx_ramps.c
@@ -0,0 +1,432 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+
+/*
+ * The following APC offset value and Tx ramp template tables have been
+ * extracted out of the one special Mot C11x fw version for which we got the
+ * linker map file with symbols, and they also appear to be correct for the
+ * closely related C155 hardware, which has exactly the same RF section
+ * including the old SKY77324 RF PA.
+ *
+ * FreeCalypso firmware running with these numbers on both C118 and C155 phones
+ * (using per-unit factory calibration records for the Tx levels) produces
+ * correct Tx output levels and ramps as verified with the CMU200 RF test
+ * instrument.
+ */
+
+uint8_t apc_offset = 32;
+
+struct txcal_ramp_def rf_tx_ramps_850[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 31, 31, 31, 3, 0},
+ /* ramp-down */
+ { 31, 31, 18, 22, 6, 10, 2, 1, 1, 3, 3, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 31, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 6, 8, 8, 9, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 31, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 29, 0, 0},
+ /* ramp-down */
+ { 31, 25, 21, 20, 13, 14, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 31, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 22, 0, 0},
+ /* ramp-down */
+ { 27, 28, 23, 19, 13, 14, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 31, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 14, 0, 0},
+ /* ramp-down */
+ { 31, 21, 31, 2, 31, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 31, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 21, 31, 31, 2, 31, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 31, 31, 7, 0, 0, 0, 0, 0, 0, 0, 0, 31, 28, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 28, 14, 3, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 31, 31, 16, 0, 0, 0, 0, 0, 0, 0, 0, 31, 19, 0, 0, 0},
+ /* ramp-down */
+ { 20, 30, 30, 10, 28, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 31, 31, 26, 0, 0, 0, 0, 0, 0, 0, 0, 31, 9, 0, 0, 0},
+ /* ramp-down */
+ { 20, 26, 26, 18, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 31, 31, 31, 2, 0, 0, 0, 0, 0, 0, 0, 31, 2, 0, 0, 0},
+ /* ramp-down */
+ { 16, 16, 26, 26, 26, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 31, 31, 31, 11, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0},
+ /* ramp-down */
+ { 10, 12, 31, 26, 29, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 31, 31, 31, 18, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0},
+ /* ramp-down */
+ { 2, 20, 31, 26, 31, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 31, 31, 31, 25, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0},
+ /* ramp-down */
+ { 2, 20, 31, 26, 31, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 31, 31, 31, 30, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0},
+ /* ramp-down */
+ { 1, 16, 31, 31, 31, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0},
+ /* ramp-down */
+ { 4, 8, 10, 20, 31, 31, 20, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0},
+ /* ramp-down */
+ { 4, 8, 10, 20, 31, 31, 20, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 31, 5, 0},
+ /* ramp-down */
+ { 31, 31, 28, 15, 2, 0, 19, 2, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 26, 29, 8, 0},
+ /* ramp-down */
+ { 31, 31, 29, 14, 2, 1, 15, 2, 3, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 31, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 27, 24, 1, 0},
+ /* ramp-down */
+ { 30, 31, 25, 14, 2, 2, 15, 7, 2, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 31, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 14, 29, 1, 0},
+ /* ramp-down */
+ { 31, 29, 31, 13, 2, 2, 15, 2, 3, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 31, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 17, 19, 0, 0},
+ /* ramp-down */
+ { 31, 30, 30, 15, 1, 2, 17, 2, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 31, 31, 7, 0, 0, 0, 0, 0, 0, 0, 0, 31, 19, 7, 2, 0},
+ /* ramp-down */
+ { 29, 31, 29, 16, 4, 0, 14, 2, 1, 2, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 31, 31, 16, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 20, 0, 0},
+ /* ramp-down */
+ { 19, 26, 26, 28, 10, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 31, 31, 25, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 8, 2, 0},
+ /* ramp-down */
+ { 19, 28, 31, 24, 4, 0, 19, 3, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 31, 31, 31, 2, 0, 0, 0, 0, 0, 0, 0, 31, 2, 0, 0, 0},
+ /* ramp-down */
+ { 19, 28, 31, 24, 4, 0, 17, 5, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 31, 31, 31, 9, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0},
+ /* ramp-down */
+ { 18, 25, 28, 31, 2, 2, 19, 3, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 31, 31, 31, 16, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0},
+ /* ramp-down */
+ { 14, 21, 24, 29, 6, 2, 23, 5, 4, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 31, 31, 31, 22, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 1, 0},
+ /* ramp-down */
+ { 8, 26, 26, 28, 12, 12, 5, 5, 0, 6, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 31, 31, 31, 27, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0},
+ /* ramp-down */
+ { 8, 14, 27, 30, 20, 19, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0},
+ /* ramp-down */
+ { 9, 10, 15, 26, 25, 10, 17, 13, 3, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 31, 31, 30, 30, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 4, 15, 21, 21, 21, 21, 15, 10, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 31, 31, 30, 30, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 4, 15, 21, 21, 21, 21, 15, 10, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1800[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 27, 4, 0},
+ /* ramp-down */
+ { 28, 31, 18, 8, 8, 13, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 31, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 24, 0, 0},
+ /* ramp-down */
+ { 10, 30, 30, 20, 8, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 31, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 31, 16, 0, 0},
+ /* ramp-down */
+ { 10, 30, 31, 24, 31, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 31, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 23, 19, 0, 0},
+ /* ramp-down */
+ { 31, 14, 31, 5, 24, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 10, 21, 31, 0, 0},
+ /* ramp-down */
+ { 20, 22, 31, 10, 22, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 31, 31, 13, 0, 0, 0, 0, 0, 0, 0, 0, 31, 22, 0, 0, 0},
+ /* ramp-down */
+ { 22, 14, 26, 22, 22, 17, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 31, 31, 21, 0, 0, 0, 0, 0, 0, 0, 0, 24, 21, 0, 0, 0},
+ /* ramp-down */
+ { 10, 31, 31, 25, 17, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 28, 10, 0, 0, 0},
+ /* ramp-down */
+ { 17, 24, 28, 21, 24, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 27, 4, 0, 0, 0},
+ /* ramp-down */
+ { 9, 23, 31, 24, 24, 13, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 31, 31, 31, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 10, 0, 0},
+ /* ramp-down */
+ { 9, 23, 31, 24, 24, 13, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 31, 31, 31, 17, 0, 0, 0, 0, 0, 0, 0, 0, 12, 6, 0, 0},
+ /* ramp-down */
+ { 10, 10, 31, 31, 24, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 31, 31, 31, 21, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0},
+ /* ramp-down */
+ { 4, 14, 31, 31, 26, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 31, 31, 31, 27, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0},
+ /* ramp-down */
+ { 2, 14, 31, 31, 28, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 31, 31, 31, 29, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0},
+ /* ramp-down */
+ { 0, 6, 14, 31, 31, 24, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0},
+ /* ramp-down */
+ { 2, 4, 4, 18, 31, 31, 24, 5, 5, 4, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 3, 2, 2, 22, 22, 21, 21, 21, 9, 5, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 17, 0, 0},
+ /* ramp-down */
+ { 31, 31, 15, 25, 8, 10, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 31, 8, 0, 0, 0, 0, 0, 0, 0, 0, 5, 31, 31, 22, 0, 0},
+ /* ramp-down */
+ { 31, 21, 31, 20, 4, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 31, 16, 0, 0, 0, 0, 0, 0, 0, 0, 6, 31, 31, 13, 0, 0},
+ /* ramp-down */
+ { 30, 31, 24, 31, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 31, 24, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 31, 31, 19, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 31, 31, 2, 0, 0, 0, 0, 0, 0, 0, 6, 31, 22, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 14, 24, 5, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 31, 31, 10, 0, 0, 0, 0, 0, 0, 0, 0, 31, 25, 0, 0, 0},
+ /* ramp-down */
+ { 31, 19, 20, 8, 24, 17, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 31, 30, 19, 0, 0, 0, 0, 0, 0, 0, 0, 31, 17, 0, 0, 0},
+ /* ramp-down */
+ { 2, 31, 31, 25, 17, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 31, 31, 26, 0, 0, 0, 0, 0, 0, 0, 0, 31, 9, 0, 0, 0},
+ /* ramp-down */
+ { 14, 24, 25, 30, 24, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 31, 31, 31, 2, 0, 0, 0, 0, 0, 0, 0, 31, 2, 0, 0, 0},
+ /* ramp-down */
+ { 12, 17, 27, 31, 24, 13, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 31, 31, 30, 10, 0, 0, 0, 0, 0, 0, 0, 25, 1, 0, 0, 0},
+ /* ramp-down */
+ { 21, 31, 31, 26, 13, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 31, 31, 31, 11, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0},
+ /* ramp-down */
+ { 14, 31, 31, 28, 13, 5, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 31, 31, 31, 19, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0},
+ /* ramp-down */
+ { 6, 14, 31, 31, 24, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 31, 31, 31, 25, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0},
+ /* ramp-down */
+ { 6, 14, 31, 31, 24, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 31, 31, 31, 29, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0},
+ /* ramp-down */
+ { 6, 14, 31, 31, 24, 13, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0},
+ /* ramp-down */
+ { 3, 16, 31, 31, 24, 14, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 4, 6, 21, 21, 21, 21, 15, 15, 4, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
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
new file mode 100644
index 00000000..c4a113fd
--- /dev/null
+++ b/src/target/firmware/board/compal_e99/readcal.c
@@ -0,0 +1,26 @@
+/*
+ * 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/readcal.h>
+
+extern void read_compal_factory_records(uint32_t flash_addr);
+
+void read_factory_rf_calibration(void)
+{
+ read_compal_factory_records(0x7E0000);
+}
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
new file mode 100644
index 00000000..5bf68acf
--- /dev/null
+++ b/src/target/firmware/board/gta0x/afcparams.c
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 typical set of AFC Psi parameters for an FCDEV3B modem board,
+ * computed by FreeCalypso fc-rfcal-vcxo calibration tool from frequency
+ * offset measurements made with a CMU200 RF tester:
+ *
+ * Psi_sta_inv: 3462
+ * Psi_st: 15
+ * Psi_st_32: 992326
+ * Psi_st_inv: 4328
+ *
+ * The following AFC slope number is the closest OsmocomBB-style afc_slope
+ * integer corresponding to these Psi numbers; the true value is somewhere
+ * between 454 and 455.
+ *
+ * This AFC slope setting is expected to be correct for both Openmoko and
+ * FreeCalypso hardware as we use the same VCXO components as were used
+ * by Openmoko.
+ */
+int16_t afc_slope = 454;
+
+/*
+ * 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/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_power.c b/src/target/firmware/board/gta0x/rf_power.c
deleted file mode 100644
index 1c896f74..00000000
--- a/src/target/firmware/board/gta0x/rf_power.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Tx RF power calibration for the FIC GTA0x phones */
-
-/* (C) 2010 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 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 <stdint.h>
-#include <osmocom/core/utils.h>
-
-/* GSM900 ARFCN 33, Measurements by Steve Markgraf / May 2010 */
-/* FIXME those are from compal ... need real GTA calibration */
-const int16_t dbm2apc_gsm900[] = {
- [0] = 151,
- [1] = 152,
- [2] = 153,
- [3] = 155,
- [4] = 156,
- [5] = 158,
- [6] = 160,
- [7] = 162,
- [8] = 164,
- [9] = 167,
- [10] = 170,
- [11] = 173,
- [12] = 177,
- [13] = 182,
- [14] = 187,
- [15] = 192,
- [16] = 199,
- [17] = 206,
- [18] = 214,
- [19] = 223,
- [20] = 233,
- [21] = 244,
- [22] = 260,
- [23] = 271,
- [24] = 288,
- [25] = 307,
- [26] = 327,
- [27] = 350,
- [28] = 376,
- [29] = 407,
- [30] = 456,
- [31] = 575,
-};
-
-const int dbm2apc_gsm900_max = ARRAY_SIZE(dbm2apc_gsm900) - 1;
diff --git a/src/target/firmware/board/gta0x/rf_tables.c b/src/target/firmware/board/gta0x/rf_tables.c
new file mode 100644
index 00000000..650c77c7
--- /dev/null
+++ b/src/target/firmware/board/gta0x/rf_tables.c
@@ -0,0 +1,569 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+
+/* APC offset (comes from the official firmware) for TI-classic targets */
+uint8_t apc_offset = 48;
+
+/*
+ * The following Tx levels and ramps tables are the ones compiled into
+ * the official Openmoko and FreeCalypso firmwares for GTA0x and FCDEV3B
+ * devices; these are the tables which the firmware uses in the absence
+ * of per-unit calibration files in FFS. Any /gsm/rf/tx/levels.* files
+ * found in FFS directly overwrite the compiled-in levels tables, and
+ * any /gsm/rf/tx/ramps.* files likewise directly overwrite the
+ * compiled-in ramps tables.
+ */
+struct txcal_tx_level rf_tx_levels_850[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 507, 0, 0 }, /* 0 */
+ { 507, 0, 0 }, /* 1 */
+ { 507, 0, 0 }, /* 2 */
+ { 507, 0, 0 }, /* 3 */
+ { 507, 0, 0 }, /* 4 */
+ { 507, 0, 0 }, /* 5 */
+ { 417, 1, 0 }, /* 6 */
+ { 350, 2, 0 }, /* 7 */
+ { 282, 3, 0 }, /* 8 */
+ { 226, 4, 0 }, /* 9 */
+ { 183, 5, 0 }, /* 10 */
+ { 148, 6, 0 }, /* 11 */
+ { 121, 7, 0 }, /* 12 */
+ { 98, 8, 0 }, /* 13 */
+ { 80, 9, 0 }, /* 14 */
+ { 66, 10, 0 }, /* 15 */
+ { 54, 11, 0 }, /* 16 */
+ { 44, 12, 0 }, /* 17 */
+ { 36, 13, 0 }, /* 18 */
+ { 29, 14, 0 }, /* 19 */
+ { 29, 14, 0 }, /* 20 */
+ { 29, 14, 0 }, /* 21 */
+ { 29, 14, 0 }, /* 22 */
+ { 29, 14, 0 }, /* 23 */
+ { 29, 14, 0 }, /* 24 */
+ { 29, 14, 0 }, /* 25 */
+ { 29, 14, 0 }, /* 26 */
+ { 29, 14, 0 }, /* 27 */
+ { 29, 14, 0 }, /* 28 */
+ { 29, 14, 0 }, /* 29 */
+ { 29, 14, 0 }, /* 30 */
+ { 29, 14, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_900[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 465, 0, 0 }, /* 0 */
+ { 465, 0, 0 }, /* 1 */
+ { 465, 0, 0 }, /* 2 */
+ { 465, 0, 0 }, /* 3 */
+ { 465, 0, 0 }, /* 4 */
+ { 465, 0, 0 }, /* 5 */
+ { 387, 1, 0 }, /* 6 */
+ { 324, 2, 0 }, /* 7 */
+ { 260, 3, 0 }, /* 8 */
+ { 210, 4, 0 }, /* 9 */
+ { 170, 5, 0 }, /* 10 */
+ { 138, 6, 0 }, /* 11 */
+ { 113, 7, 0 }, /* 12 */
+ { 92, 8, 0 }, /* 13 */
+ { 76, 9, 0 }, /* 14 */
+ { 62, 10, 0 }, /* 15 */
+ { 51, 11, 0 }, /* 16 */
+ { 42, 12, 0 }, /* 17 */
+ { 34, 13, 0 }, /* 18 */
+ { 27, 14, 0 }, /* 19 */
+ { 27, 14, 0 }, /* 20 */
+ { 27, 14, 0 }, /* 21 */
+ { 27, 14, 0 }, /* 22 */
+ { 27, 14, 0 }, /* 23 */
+ { 27, 14, 0 }, /* 24 */
+ { 27, 14, 0 }, /* 25 */
+ { 27, 14, 0 }, /* 26 */
+ { 27, 14, 0 }, /* 27 */
+ { 27, 14, 0 }, /* 28 */
+ { 27, 14, 0 }, /* 29 */
+ { 27, 14, 0 }, /* 30 */
+ { 27, 14, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_1800[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 436, 0, 0 }, /* 0 */
+ { 363, 1, 0 }, /* 1 */
+ { 310, 2, 0 }, /* 2 */
+ { 253, 3, 0 }, /* 3 */
+ { 205, 4, 0 }, /* 4 */
+ { 168, 5, 0 }, /* 5 */
+ { 138, 6, 0 }, /* 6 */
+ { 113, 7, 0 }, /* 7 */
+ { 93, 8, 0 }, /* 8 */
+ { 76, 9, 0 }, /* 9 */
+ { 61, 10, 0 }, /* 10 */
+ { 50, 11, 0 }, /* 11 */
+ { 40, 12, 0 }, /* 12 */
+ { 32, 13, 0 }, /* 13 */
+ { 26, 14, 0 }, /* 14 */
+ { 20, 15, 0 }, /* 15 */
+ { 20, 15, 0 }, /* 16 */
+ { 20, 15, 0 }, /* 17 */
+ { 20, 15, 0 }, /* 18 */
+ { 20, 15, 0 }, /* 19 */
+ { 20, 15, 0 }, /* 20 */
+ { 20, 15, 0 }, /* 21 */
+ { 20, 15, 0 }, /* 22 */
+ { 20, 15, 0 }, /* 23 */
+ { 20, 15, 0 }, /* 24 */
+ { 20, 15, 0 }, /* 25 */
+ { 20, 15, 0 }, /* 26 */
+ { 20, 15, 0 }, /* 27 */
+ { 20, 15, 0 }, /* 28 */
+ { 20, 0, 0 }, /* 29 */
+ { 20, 0, 0 }, /* 30 */
+ { 20, 0, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_1900[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 429, 0, 0 }, /* 0 */
+ { 353, 1, 0 }, /* 1 */
+ { 302, 2, 0 }, /* 2 */
+ { 246, 3, 0 }, /* 3 */
+ { 200, 4, 0 }, /* 4 */
+ { 164, 5, 0 }, /* 5 */
+ { 135, 6, 0 }, /* 6 */
+ { 111, 7, 0 }, /* 7 */
+ { 91, 8, 0 }, /* 8 */
+ { 75, 9, 0 }, /* 9 */
+ { 60, 10, 0 }, /* 10 */
+ { 49, 11, 0 }, /* 11 */
+ { 40, 12, 0 }, /* 12 */
+ { 33, 13, 0 }, /* 13 */
+ { 26, 14, 0 }, /* 14 */
+ { 26, 15, 0 }, /* 15 */
+ { 26, 15, 0 }, /* 16 */
+ { 26, 15, 0 }, /* 17 */
+ { 26, 15, 0 }, /* 18 */
+ { 26, 15, 0 }, /* 19 */
+ { 26, 15, 0 }, /* 20 */
+ { 26, 15, 0 }, /* 21 */
+ { 26, 15, 0 }, /* 22 */
+ { 26, 15, 0 }, /* 23 */
+ { 26, 15, 0 }, /* 24 */
+ { 26, 15, 0 }, /* 25 */
+ { 26, 15, 0 }, /* 26 */
+ { 26, 15, 0 }, /* 27 */
+ { 26, 15, 0 }, /* 28 */
+ { 26, 0, 0 }, /* 29 */
+ { 26, 0, 0 }, /* 30 */
+ { 26, 0, 0 }, /* 31 */
+};
+
+struct txcal_ramp_def rf_tx_ramps_850[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 9, 18, 25, 31, 30, 15, 0, 0},
+ /* ramp-down */
+ { 0, 11, 31, 31, 31, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 1, 1, 7, 16, 28, 31, 31, 13, 0, 0},
+ /* ramp-down */
+ { 0, 8, 31, 31, 31, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 1, 1, 8, 16, 29, 31, 31, 11, 0, 0},
+ /* ramp-down */
+ { 0, 8, 28, 31, 31, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 0, 6, 18, 28, 31, 31, 12, 0, 0},
+ /* ramp-down */
+ { 0, 9, 24, 31, 31, 31, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 3, 0, 5, 19, 31, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 0, 7, 18, 31, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 3, 0, 5, 20, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 10, 21, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 4, 0, 9, 23, 22, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 9, 24, 30, 31, 30, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 5, 0, 8, 21, 24, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 8, 23, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 5, 0, 3, 1, 27, 22, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 8, 27, 25, 26, 31, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 5, 0, 0, 2, 7, 22, 23, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 7, 25, 30, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 5, 0, 4, 8, 21, 21, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 8, 21, 31, 31, 31, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 7, 0, 0, 12, 22, 25, 31, 27, 4, 0, 0},
+ /* ramp-down */
+ { 0, 9, 12, 21, 31, 31, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 7, 0, 8, 15, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 0, 6, 14, 23, 31, 31, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 20, 0, 0, 8, 15, 14, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 9, 18, 25, 31, 30, 15, 0, 0},
+ /* ramp-down */
+ { 0, 11, 31, 31, 31, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 1, 1, 7, 16, 28, 31, 31, 13, 0, 0},
+ /* ramp-down */
+ { 0, 8, 31, 31, 31, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 1, 1, 8, 16, 29, 31, 31, 11, 0, 0},
+ /* ramp-down */
+ { 0, 8, 28, 31, 31, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 0, 6, 18, 28, 31, 31, 12, 0, 0},
+ /* ramp-down */
+ { 0, 9, 24, 31, 31, 31, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 3, 0, 5, 19, 31, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 0, 7, 18, 31, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 3, 0, 5, 20, 31, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 10, 21, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 4, 0, 9, 23, 22, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 9, 24, 30, 31, 30, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 5, 0, 8, 21, 24, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 8, 23, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 5, 0, 3, 1, 27, 22, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 8, 27, 25, 26, 31, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 5, 0, 0, 2, 7, 22, 23, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 7, 25, 30, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 5, 0, 4, 8, 21, 21, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 8, 21, 31, 31, 31, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 7, 0, 0, 12, 22, 25, 31, 27, 4, 0, 0},
+ /* ramp-down */
+ { 0, 9, 12, 21, 31, 31, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 7, 0, 8, 15, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 0, 6, 14, 23, 31, 31, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 20, 0, 0, 8, 15, 14, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1800[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 3, 5, 16, 31, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 11, 31, 31, 31, 10, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 3, 4, 17, 30, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 0, 10, 31, 31, 31, 13, 9, 3, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 4, 2, 2, 18, 31, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 10, 26, 31, 31, 16, 10, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 3, 4, 4, 15, 31, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 9, 31, 31, 31, 13, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 4, 3, 7, 11, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 0, 8, 31, 31, 31, 11, 9, 7, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 4, 3, 2, 7, 14, 25, 31, 31, 11, 0, 0},
+ /* ramp-down */
+ { 0, 14, 31, 31, 31, 9, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 7, 1, 3, 10, 12, 25, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 7, 30, 31, 31, 14, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 3, 5, 0, 5, 8, 12, 26, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 15, 0, 8, 5, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 9, 0, 3, 10, 16, 21, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 11, 28, 31, 27, 10, 11, 0, 10, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 10, 0, 6, 9, 15, 22, 29, 31, 6, 0, 0},
+ /* ramp-down */
+ { 0, 9, 22, 31, 31, 12, 5, 0, 18, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 14, 0, 0, 8, 6, 20, 21, 29, 24, 6, 0, 0},
+ /* ramp-down */
+ { 0, 8, 28, 29, 26, 14, 6, 0, 17, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 16, 0, 3, 5, 8, 16, 31, 28, 18, 3, 0, 0},
+ /* ramp-down */
+ { 0, 6, 18, 26, 31, 16, 9, 7, 0, 15, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 19, 0, 3, 6, 8, 21, 24, 31, 14, 2, 0, 0},
+ /* ramp-down */
+ { 0, 0, 12, 31, 31, 27, 4, 0, 23, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 14, 14, 0, 0, 24, 31, 31, 14, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 11, 31, 31, 22, 11, 3, 19, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 30, 1, 4, 8, 18, 31, 31, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 8, 31, 31, 22, 5, 0, 31, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 31, 13, 0, 0, 14, 31, 31, 8, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 4, 31, 31, 25, 5, 0, 5, 26, 1, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 7, 0, 0, 16, 31, 31, 31, 12, 0, 0},
+ /* ramp-down */
+ { 0, 13, 31, 31, 31, 18, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 2, 3, 4, 17, 30, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 0, 10, 31, 31, 31, 13, 9, 3, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 4, 2, 2, 18, 31, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 10, 26, 31, 31, 16, 10, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 3, 4, 4, 15, 31, 31, 31, 9, 0, 0},
+ /* ramp-down */
+ { 0, 9, 31, 31, 31, 13, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 4, 3, 0, 18, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 0, 8, 31, 31, 31, 11, 9, 7, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 4, 3, 2, 7, 14, 25, 31, 31, 11, 0, 0},
+ /* ramp-down */
+ { 0, 14, 31, 31, 31, 9, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 7, 1, 3, 10, 12, 25, 31, 31, 8, 0, 0},
+ /* ramp-down */
+ { 0, 7, 30, 31, 31, 14, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 3, 5, 0, 5, 8, 12, 26, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 7, 31, 31, 31, 15, 0, 8, 5, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 9, 0, 3, 10, 16, 21, 31, 31, 7, 0, 0},
+ /* ramp-down */
+ { 0, 11, 28, 31, 27, 10, 11, 0, 10, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 10, 0, 6, 9, 15, 22, 29, 31, 6, 0, 0},
+ /* ramp-down */
+ { 0, 9, 22, 31, 31, 12, 5, 0, 18, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 14, 0, 0, 4, 10, 20, 21, 29, 24, 6, 0, 0},
+ /* ramp-down */
+ { 0, 8, 28, 29, 26, 14, 6, 0, 17, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 16, 0, 3, 5, 8, 16, 31, 28, 18, 3, 0, 0},
+ /* ramp-down */
+ { 0, 6, 18, 26, 31, 16, 9, 7, 0, 15, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 19, 0, 3, 6, 8, 21, 24, 31, 14, 2, 0, 0},
+ /* ramp-down */
+ { 0, 0, 12, 31, 31, 27, 4, 0, 23, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 14, 14, 0, 0, 24, 31, 31, 14, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 11, 31, 31, 22, 11, 3, 19, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 30, 1, 4, 8, 18, 31, 31, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 8, 31, 31, 22, 5, 0, 31, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 30, 1, 4, 8, 18, 31, 31, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 8, 31, 31, 22, 5, 0, 31, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
diff --git a/src/target/firmware/board/gta0x/rffe_gta0x_triband.c b/src/target/firmware/board/gta0x/rffe_gta0x_triband.c
index b520f656..dcb9ca9d 100644
--- a/src/target/firmware/board/gta0x/rffe_gta0x_triband.c
+++ b/src/target/firmware/board/gta0x/rffe_gta0x_triband.c
@@ -7,10 +7,22 @@
#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
+/*
+ * 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 RF tract is identical between Openmoko
+ * GTA0x and FreeCalypso FCDEV3B boards, both manufacturers' devices
+ * have had their GMagic calibrated per unit at the center frequency
+ * of each supported downlink band at the respective factories, and all
+ * calibrated values on defect-free units fall in the range between 199
+ * to 202, with 200 as the round median value. Setting OsmocomBB's notion
+ * of system inherent gain to 73 dB produces an equivalent of GMagic=200
+ * in TI's universe, which is more correct than the previous setting of
+ * 71 dB copied from Compal/Motorola phones, which have a different RFFE.
+ */
+#define SYSTEM_INHERENT_GAIN 73
/* describe how the RF frontend is wired on the Openmoko GTA0x boards */
diff --git a/src/target/firmware/board/gtm900b/afcparams.c b/src/target/firmware/board/gtm900b/afcparams.c
new file mode 100644
index 00000000..9dbbe7e9
--- /dev/null
+++ b/src/target/firmware/board/gtm900b/afcparams.c
@@ -0,0 +1,43 @@
+/*
+ * 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 Huawei on a GTM900-B MGC2GSMT module, as recorded
+ * in the /gsm/rf/afcparams file:
+ *
+ * Psi_sta_inv: 13626
+ * Psi_st: 4
+ * Psi_st_32: 252168
+ * Psi_st_inv: 17032
+ *
+ * The following AFC slope number is the closest OsmocomBB-style afc_slope
+ * integer corresponding to these Psi numbers; the true value is somewhere
+ * between 115 and 116.
+ */
+int16_t afc_slope = 115;
+
+/*
+ * 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/gtm900b/init.c b/src/target/firmware/board/gtm900b/init.c
index 38ede10c..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>
@@ -50,11 +46,14 @@
#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
#define LPG_LCR_REG 0xfffe7800
#define LPG_PM_REG 0xfffe7801
+int gtm900_hw_is_mg01gsmt;
+
static void board_io_init(void)
{
uint16_t reg;
@@ -67,25 +66,100 @@ static void board_io_init(void)
writew(reg, ASIC_CONF_REG);
/*
- * Most Calypso peripheral interface signals are unconnected
- * on this modem. We configure them to be GPIOs in IO_CONF_REG,
- * then configure them to be outputs in IO_CNTL_REG, then set
- * the outputs to 0 in ARMIO_LATCH_OUT.
+ * Configure Calypso GPIO and multifunction pins the same way
+ * how Huawei's official firmware configures them.
*/
writew(0x03F5, IO_CONF_REG);
- writew(0xC000, IO_CNTL_REG);
- writew(0x0000, ARMIO_LATCH_OUT);
+ writew(0xDC58, IO_CNTL_REG);
+ writew(0x0007, ARMIO_LATCH_OUT);
/* Set LPG output permanently on (power LED) */
writew(1, LPG_PM_REG);
writew((1 << 7), LPG_LCR_REG);
+
+ /* configure ADD(22), needed for second half of flash on MG01GSMT */
+ reg = readw(ARM_CONF_REG);
+ reg |= (1 << 3);
+ writew(reg, ARM_CONF_REG);
+}
+
+/*
+ * There exist two firmware-incompatible versions of GTM900-B hardware:
+ * MG01GSMT and MGCxGSMT. They have different flash chip types (8 MiB
+ * vs. 4 MiB) with correspondingly different TIFFS configurations
+ * (and we need TIFFS in order to read factory RF calibration values),
+ * and they have different (incompatible) RFFE control signals.
+ *
+ * We are going to check the flash chip type and use it to decide which
+ * hw variant we are running on.
+ */
+static void board_flash_init(void)
+{
+ uint16_t manufacturer_id, device_id[3];
+
+ /* Use an address above the Calypso boot ROM
+ * so we don't need to unmap it to access the flash. */
+ flash_get_id((void *)0x40000, &manufacturer_id, device_id);
+
+ switch (manufacturer_id) {
+ case CFI_MANUF_SPANSION:
+ /* is it S71PL064J? */
+ if (device_id[0] == 0x227E && device_id[1] == 0x2202 &&
+ device_id[2] == 0x2201) {
+ gtm900_hw_is_mg01gsmt = 1;
+ break;
+ }
+ /* is it S71PL032J? */
+ if (device_id[0] == 0x227E && device_id[1] == 0x220A &&
+ device_id[2] == 0x2201) {
+ gtm900_hw_is_mg01gsmt = 0;
+ break;
+ }
+ goto bad;
+ case CFI_MANUF_SAMSUNG:
+ /* is it K5A3281CTM? */
+ if (device_id[0] == 0x22A0) {
+ gtm900_hw_is_mg01gsmt = 0;
+ break;
+ }
+ /* is it K5L3316CAM? */
+ if (device_id[0] == 0x257E && device_id[1] == 0x2503 &&
+ device_id[2] == 0x2501) {
+ gtm900_hw_is_mg01gsmt = 0;
+ break;
+ }
+ /* FALL THRU */
+ default:
+ bad:
+ printf("Unknown module detected, "
+ "flash ID 0x%04x 0x%04x 0x%04x 0x%04x\n"
+ "Please contact mailing list!\n\n", manufacturer_id,
+ device_id[0], device_id[1], device_id[2]);
+ return;
+ }
+
+ /* Initialize TIFFS reader */
+ if (gtm900_hw_is_mg01gsmt)
+ tiffs_init(0x700000, 0x10000, 15);
+ else
+ tiffs_init(0x380000, 0x10000, 7);
}
void board_init(int with_irq)
{
- /* Configure the memory interface */
- calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1);
- calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1);
+ /*
+ * Configure the memory interface.
+ * Huawei's official fw sets WS=4 for RAM, but not for flash -
+ * but let's be consistent and use WS=4 for both. Please refer
+ * to this technical article for the underlying theory:
+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);
+ /*
+ * The remaining 3 chip selects are unused on this hw,
+ * thus their settings are dummies.
+ */
calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1);
calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1);
calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1);
@@ -137,4 +211,7 @@ void board_init(int with_irq)
/* Initialize ABB driver (uses SPI) */
twl3025_init();
+
+ /* Initialize board flash */
+ board_flash_init();
}
diff --git a/src/target/firmware/board/gtm900b/rf_power.c b/src/target/firmware/board/gtm900b/rf_power.c
deleted file mode 100644
index cd47ddf7..00000000
--- a/src/target/firmware/board/gtm900b/rf_power.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Tx RF power calibration for the Huawei GTM900-B */
-
-/* (C) 2010 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 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 <stdint.h>
-#include <osmocom/core/utils.h>
-
-/* GSM900 ARFCN 33, Measurements by Steve Markgraf / May 2010 */
-/* FIXME those are from the Compal phones, do measurements with the GTM900-B */
-const int16_t dbm2apc_gsm900[] = {
- [0] = 151,
- [1] = 152,
- [2] = 153,
- [3] = 155,
- [4] = 156,
- [5] = 158,
- [6] = 160,
- [7] = 162,
- [8] = 164,
- [9] = 167,
- [10] = 170,
- [11] = 173,
- [12] = 177,
- [13] = 182,
- [14] = 187,
- [15] = 192,
- [16] = 199,
- [17] = 206,
- [18] = 214,
- [19] = 223,
- [20] = 233,
- [21] = 244,
- [22] = 260,
- [23] = 271,
- [24] = 288,
- [25] = 307,
- [26] = 327,
- [27] = 350,
- [28] = 376,
- [29] = 407,
- [30] = 456,
- [31] = 575,
-};
-
-const int dbm2apc_gsm900_max = ARRAY_SIZE(dbm2apc_gsm900) - 1;
diff --git a/src/target/firmware/board/gtm900b/rffe_gtm900b.c b/src/target/firmware/board/gtm900b/rffe_gtm900b.c
index 633c6378..3efb5e2a 100644
--- a/src/target/firmware/board/gtm900b/rffe_gtm900b.c
+++ b/src/target/firmware/board/gtm900b/rffe_gtm900b.c
@@ -1,5 +1,5 @@
/* RF frontend driver for Huawei GTM900-B modems, supporting both
- * MG01GSMT and MG01GSMT hardware variants */
+ * MG01GSMT and MGCxGSMT hardware variants */
/* (C) 2019 by Steve Markgraf <steve@steve-m.de>
*
@@ -63,12 +63,7 @@
* Tx2: high band PA output
*/
-typedef enum rffe_var {
- RFFE_MGC2GSMT,
- RFFE_MG01GSMT
-} rffe_var_t;
-
-static rffe_var_t rffe_variant = RFFE_MGC2GSMT;
+extern int gtm900_hw_is_mg01gsmt; /* set in init.c */
static inline void rffe_mode_mgc2gsmt(enum gsm_band band, int tx)
{
@@ -139,10 +134,10 @@ static inline void rffe_mode_mg01gsmt(enum gsm_band band, int tx)
/* switch RF Frontend Mode */
void rffe_mode(enum gsm_band band, int tx)
{
- if (rffe_variant == RFFE_MGC2GSMT)
- rffe_mode_mgc2gsmt(band, tx);
- else
+ if (gtm900_hw_is_mg01gsmt)
rffe_mode_mg01gsmt(band, tx);
+ else
+ rffe_mode_mgc2gsmt(band, tx);
}
uint32_t rffe_get_rx_ports(void)
@@ -166,7 +161,6 @@ int rffe_iq_swapped(uint16_t band_arfcn, int tx)
void rffe_init(void)
{
uint16_t reg;
- uint16_t manufacturer_id = 0;
reg = readw(ARM_CONF_REG);
reg &= ~ (1 << 7); /* TSPACT4 I/O function, not nRDYMEM */
@@ -176,28 +170,6 @@ void rffe_init(void)
tsp_setup(IOTA_STROBE, 1, 0, 0);
trf6151_init(RITA_STROBE, RITA_RESET);
-
- /* Detect the used RFFE variant based on the used flash chip.
- * The MGC2GSMT uses a Samsung flash, whereas the MG01GSMT uses
- * a Spansion flash. We use an address above the Calpso bootrom
- * so we do not need to unmap it to access the flash. */
- flash_get_id((void *)0x40000, &manufacturer_id, NULL);
-
- switch (manufacturer_id) {
- case CFI_MANUF_SPANSION:
- printf("Detected MG01GSMT module\n\n");
- rffe_variant = RFFE_MG01GSMT;
- break;
- case CFI_MANUF_SAMSUNG:
- printf("Detected MGC2GSMT module\n\n");
- rffe_variant = RFFE_MGC2GSMT;
- break;
- default:
- printf("Unknown module detected, flash ID 0x%4.4x\n"
- "Please contact mailing list!\n\n", manufacturer_id);
- rffe_variant = RFFE_MGC2GSMT;
- break;
- }
}
uint8_t rffe_get_gain(void)
diff --git a/src/target/firmware/board/mediatek/ram.lds b/src/target/firmware/board/mediatek/ram.lds
index 7083c273..84568cf3 100644
--- a/src/target/firmware/board/mediatek/ram.lds
+++ b/src/target/firmware/board/mediatek/ram.lds
@@ -12,7 +12,7 @@ MEMORY
{
/* mtk-loaded binary: our text, initialized data */
LRAM (rw) : ORIGIN = 0x40000000, LENGTH = 0x00006000
- /* mtk-loaded binary: our unitialized data, stacks, heap */
+ /* mtk-loaded binary: our uninitialized data, stacks, heap */
IRAM (rw) : ORIGIN = 0x40006000, LENGTH = 0x00006000
}
SECTIONS
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
new file mode 100644
index 00000000..40f608f8
--- /dev/null
+++ b/src/target/firmware/board/pirelli_dpl10/readcal.c
@@ -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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 <rf/readcal.h>
+#include <rf/txcal.h>
+#include <rf/vcxocal.h>
+
+static int16_t afcdac_shifted;
+
+static void afcdac_postproc(void)
+{
+ afc_initial_dac_value = afcdac_shifted >> 3;
+}
+
+static int verify_checksum(const uint8_t *start, size_t len)
+{
+ const uint8_t *p, *endp;
+ uint8_t accum;
+
+ p = start;
+ endp = start + len;
+ accum = 0;
+ while (p < endp)
+ accum += *p++;
+
+ if (accum == *p)
+ return 0; /* good */
+ else
+ return -1; /* bad */
+}
+
+static const struct calmap {
+ char *desc;
+ unsigned offset;
+ size_t record_len;
+ void *buffer;
+ void (*postproc)(void);
+} rf_cal_list[] = {
+ { "afcdac", 0x528, 2, &afcdac_shifted, afcdac_postproc },
+ { "Tx ramps 900", 0x72B, 512, rf_tx_ramps_900, NULL },
+ { "Tx levels 900", 0x92C, 128, rf_tx_levels_900, NULL },
+ { "Tx calchan 900", 0x9AD, 128, rf_tx_chan_cal_900, NULL },
+ { "Tx ramps 1800", 0xA2E, 512, rf_tx_ramps_1800, NULL },
+ { "Tx levels 1800", 0xC2F, 128, rf_tx_levels_1800, NULL },
+ { "Tx calchan 1800", 0xCB0, 128, rf_tx_chan_cal_1800, NULL },
+ { "Tx ramps 1900", 0xD31, 512, rf_tx_ramps_1900, NULL },
+ { "Tx levels 1900", 0xF32, 128, rf_tx_levels_1900, NULL },
+ { "Tx calchan 1900", 0xFB3, 128, rf_tx_chan_cal_1900, NULL },
+ { NULL, 0, 0, NULL, NULL }
+};
+
+void read_factory_rf_calibration(void)
+{
+ const struct calmap *tp;
+ const uint8_t *record;
+
+ puts("Checking factory data block for the RF calibration records\n");
+ for (tp = rf_cal_list; tp->desc; tp++) {
+ record = (const uint8_t *)0x027F0000 + tp->offset;
+ if (verify_checksum(record, tp->record_len) < 0)
+ continue;
+ printf("Found '%s' record, applying\n", tp->desc);
+ memcpy(tp->buffer, record, tp->record_len);
+ if (tp->postproc)
+ tp->postproc();
+ }
+}
diff --git a/src/target/firmware/board/pirelli_dpl10/rf_power.c b/src/target/firmware/board/pirelli_dpl10/rf_power.c
deleted file mode 100644
index 9b89847d..00000000
--- a/src/target/firmware/board/pirelli_dpl10/rf_power.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Tx RF power calibration for the Pirelli DP-L10 */
-
-/* (C) 2010 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 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 <stdint.h>
-#include <osmocom/core/utils.h>
-
-/* GSM900 ARFCN 33, Measurements by Steve Markgraf / May 2010 */
-/* FIXME those are from the Compal phones, do measurements with the DP-L10 */
-const int16_t dbm2apc_gsm900[] = {
- [0] = 151,
- [1] = 152,
- [2] = 153,
- [3] = 155,
- [4] = 156,
- [5] = 158,
- [6] = 160,
- [7] = 162,
- [8] = 164,
- [9] = 167,
- [10] = 170,
- [11] = 173,
- [12] = 177,
- [13] = 182,
- [14] = 187,
- [15] = 192,
- [16] = 199,
- [17] = 206,
- [18] = 214,
- [19] = 223,
- [20] = 233,
- [21] = 244,
- [22] = 260,
- [23] = 271,
- [24] = 288,
- [25] = 307,
- [26] = 327,
- [27] = 350,
- [28] = 376,
- [29] = 407,
- [30] = 456,
- [31] = 575,
-};
-
-const int dbm2apc_gsm900_max = ARRAY_SIZE(dbm2apc_gsm900) - 1;
diff --git a/src/target/firmware/board/pirelli_dpl10/rf_tables.c b/src/target/firmware/board/pirelli_dpl10/rf_tables.c
new file mode 100644
index 00000000..a2d26b1b
--- /dev/null
+++ b/src/target/firmware/board/pirelli_dpl10/rf_tables.c
@@ -0,0 +1,593 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+#include <rf/vcxocal.h>
+
+/*
+ * Pirelli's official fw runs with the following Psi parameters, as read out
+ * of a running fw via rftr 9 Test Mode command:
+ *
+ * Psi_sta_inv: 6974
+ * Psi_st: 8
+ * Psi_st_32: 492713
+ * Psi_st_inv: 8717
+ *
+ * The following AFC slope number is the closest OsmocomBB-style afc_slope
+ * integer corresponding to these Psi numbers; the true value is somewhere
+ * between 225 and 226.
+ */
+int16_t afc_slope = 226;
+
+/*
+ * 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
+ * Pirelli's factory data block.
+ */
+int16_t afc_initial_dac_value = -700;
+
+/* APC offset (comes from the official firmware) for Pirelli targets */
+uint8_t apc_offset = 0;
+
+/*
+ * The following Tx levels and ramps tables are the ones compiled into
+ * Pirelli's firmware; these are the tables which the firmware uses in
+ * the absence of per-unit calibration records in the factory data block
+ * in the last 64 KiB sector of the flash. In normal operation the
+ * records read from that block fully override all of these compiled-in
+ * tables, with the exception of 850 MHz bands ones - the Pirelli DP-L10
+ * does not support that band, and there are no calibration records for it.
+ */
+struct txcal_tx_level rf_tx_levels_850[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 600, 0, 0 }, /* 0 */
+ { 600, 0, 0 }, /* 1 */
+ { 600, 0, 0 }, /* 2 */
+ { 600, 0, 0 }, /* 3 */
+ { 600, 0, 0 }, /* 4 */
+ { 600, 0, 0 }, /* 5 */
+ { 540, 1, 0 }, /* 6 */
+ { 450, 2, 0 }, /* 7 */
+ { 385, 3, 0 }, /* 8 */
+ { 330, 4, 0 }, /* 9 */
+ { 285, 5, 0 }, /* 10 */
+ { 250, 6, 0 }, /* 11 */
+ { 220, 7, 0 }, /* 12 */
+ { 195, 8, 0 }, /* 13 */
+ { 175, 9, 0 }, /* 14 */
+ { 160, 10, 0 }, /* 15 */
+ { 145, 11, 0 }, /* 16 */
+ { 133, 12, 0 }, /* 17 */
+ { 123, 13, 0 }, /* 18 */
+ { 115, 14, 0 }, /* 19 */
+ { 115, 14, 0 }, /* 20 */
+ { 115, 14, 0 }, /* 21 */
+ { 115, 14, 0 }, /* 22 */
+ { 115, 14, 0 }, /* 23 */
+ { 115, 14, 0 }, /* 24 */
+ { 115, 14, 0 }, /* 25 */
+ { 115, 14, 0 }, /* 26 */
+ { 115, 14, 0 }, /* 27 */
+ { 115, 14, 0 }, /* 28 */
+ { 115, 14, 0 }, /* 29 */
+ { 115, 14, 0 }, /* 30 */
+ { 115, 14, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_900[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 570, 0, 0 }, /* 0 */
+ { 570, 0, 0 }, /* 1 */
+ { 570, 0, 0 }, /* 2 */
+ { 570, 0, 0 }, /* 3 */
+ { 570, 0, 0 }, /* 4 */
+ { 570, 0, 0 }, /* 5 */
+ { 496, 1, 1 }, /* 6 */
+ { 437, 2, 1 }, /* 7 */
+ { 385, 3, 1 }, /* 8 */
+ { 339, 4, 1 }, /* 9 */
+ { 299, 5, 2 }, /* 10 */
+ { 263, 6, 2 }, /* 11 */
+ { 232, 7, 2 }, /* 12 */
+ { 204, 8, 2 }, /* 13 */
+ { 180, 9, 2 }, /* 14 */
+ { 159, 10, 3 }, /* 15 */
+ { 141, 11, 3 }, /* 16 */
+ { 125, 12, 3 }, /* 17 */
+ { 111, 13, 3 }, /* 18 */
+ { 99, 14, 3 }, /* 19 */
+ { 99, 14, 0 }, /* 20 */
+ { 99, 14, 0 }, /* 21 */
+ { 99, 14, 0 }, /* 22 */
+ { 99, 14, 0 }, /* 23 */
+ { 99, 14, 0 }, /* 24 */
+ { 99, 14, 0 }, /* 25 */
+ { 99, 14, 0 }, /* 26 */
+ { 99, 14, 0 }, /* 27 */
+ { 99, 14, 0 }, /* 28 */
+ { 99, 14, 0 }, /* 29 */
+ { 99, 14, 0 }, /* 30 */
+ { 99, 14, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_1800[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 640, 0, 0 }, /* 0 */
+ { 558, 1, 0 }, /* 1 */
+ { 493, 2, 1 }, /* 2 */
+ { 435, 3, 1 }, /* 3 */
+ { 384, 4, 1 }, /* 4 */
+ { 338, 5, 1 }, /* 5 */
+ { 297, 6, 1 }, /* 6 */
+ { 261, 7, 1 }, /* 7 */
+ { 228, 8, 2 }, /* 8 */
+ { 199, 9, 2 }, /* 9 */
+ { 174, 10, 3 }, /* 10 */
+ { 151, 11, 3 }, /* 11 */
+ { 132, 12, 3 }, /* 12 */
+ { 115, 13, 3 }, /* 13 */
+ { 100, 14, 3 }, /* 14 */
+ { 87, 15, 3 }, /* 15 */
+ { 87, 15, 0 }, /* 16 */
+ { 87, 15, 0 }, /* 17 */
+ { 87, 15, 0 }, /* 18 */
+ { 87, 15, 0 }, /* 19 */
+ { 87, 15, 0 }, /* 20 */
+ { 87, 15, 0 }, /* 21 */
+ { 87, 15, 0 }, /* 22 */
+ { 87, 15, 0 }, /* 23 */
+ { 87, 15, 0 }, /* 24 */
+ { 87, 15, 0 }, /* 25 */
+ { 87, 15, 0 }, /* 26 */
+ { 87, 15, 0 }, /* 27 */
+ { 87, 15, 0 }, /* 28 */
+ { 87, 0, 0 }, /* 29 */
+ { 87, 0, 0 }, /* 30 */
+ { 87, 0, 0 }, /* 31 */
+};
+
+struct txcal_tx_level rf_tx_levels_1900[RF_TX_LEVELS_TABLE_SIZE] = {
+ { 577, 0, 0 }, /* 0 */
+ { 493, 1, 0 }, /* 1 */
+ { 432, 2, 0 }, /* 2 */
+ { 379, 3, 0 }, /* 3 */
+ { 335, 4, 1 }, /* 4 */
+ { 291, 5, 1 }, /* 5 */
+ { 255, 6, 1 }, /* 6 */
+ { 222, 7, 2 }, /* 7 */
+ { 194, 8, 2 }, /* 8 */
+ { 169, 9, 2 }, /* 9 */
+ { 147, 10, 2 }, /* 10 */
+ { 128, 11, 2 }, /* 11 */
+ { 111, 12, 3 }, /* 12 */
+ { 97, 13, 3 }, /* 13 */
+ { 85, 14, 3 }, /* 14 */
+ { 74, 15, 3 }, /* 15 */
+ { 101, 15, 0 }, /* 16 */
+ { 101, 15, 0 }, /* 17 */
+ { 101, 15, 0 }, /* 18 */
+ { 101, 15, 0 }, /* 19 */
+ { 101, 15, 0 }, /* 20 */
+ { 101, 15, 0 }, /* 21 */
+ { 101, 15, 0 }, /* 22 */
+ { 101, 15, 0 }, /* 23 */
+ { 101, 15, 0 }, /* 24 */
+ { 101, 15, 0 }, /* 25 */
+ { 101, 15, 0 }, /* 26 */
+ { 101, 15, 0 }, /* 27 */
+ { 101, 15, 0 }, /* 28 */
+ { 101, 0, 0 }, /* 29 */
+ { 101, 0, 0 }, /* 30 */
+ { 101, 0, 0 }, /* 31 */
+};
+
+struct txcal_ramp_def rf_tx_ramps_850[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 15, 10, 3, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 19, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 19, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 19, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 19, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 8, 0, 0, 0, 0, 0, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 12, 0, 0, 0, 0, 0, 25, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 16, 0, 0, 0, 0, 0, 21, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 20, 0, 0, 0, 0, 0, 17, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 24, 0, 0, 0, 0, 0, 13, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1800[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 0, 0, 0, 19, 0, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 0, 0, 0, 19, 0, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 0, 0, 0, 19, 0, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 0, 0, 0, 19, 0, 0, 0, 0, 1, 19, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 0, 0, 0, 0, 0, 8, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 8, 0, 0, 0, 0, 0, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 8, 0, 0, 0, 0, 0, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 8, 0, 0, 0, 0, 0, 29, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 0, 0, 0, 31, 12, 0, 0, 0, 0, 0, 25, 31, 29, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 14, 0, 0, 1, 19, 29, 31, 29, 5, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 19, 9, 31, 29, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
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
new file mode 100644
index 00000000..387febb9
--- /dev/null
+++ b/src/target/firmware/board/se_j100/tx_ramps.c
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ *
+ * Tweaked (coding style changes) by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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/txcal.h>
+
+/*
+ * The APC offset value used by SE J100 official fw is different from the
+ * Mot C1xx value, and this different APC offset value must be used by any
+ * aftermarket fw on this target in order for the Tx levels from Compal's
+ * factory records to apply correctly. This correct value has been determined
+ * by breaking into a running SE J100 fw with tfc139, running fc-loadtool
+ * with all ABB register state still intact from the interrupted official fw,
+ * and reading the APCOFF register with the abbr command.
+ */
+uint8_t apc_offset = 52;
+
+/*
+ * The following tables of Tx ramp templates have been extracted from
+ * SE J100 fw version R1C004 (R1C004-se.bin in the j100-flashimg-r1.zip
+ * FTP release); Compal's modified versions of TI's rf_XXX structures
+ * begin at address 0x5E898 in this J100 fw version.
+ *
+ * Please note that these SE J100 Tx ramp templates are different from
+ * both C11x/12x/155/156 (SKY77324) and C139/140 (SKY77325) versions.
+ */
+struct txcal_ramp_def rf_tx_ramps_850[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0, 0},
+ /* ramp-down */
+ { 15, 31, 31, 20, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 20, 31, 31, 15, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 25, 31, 31, 10, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0, 0},
+ /* ramp-down */
+ { 29, 31, 31, 31, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0, 0},
+ /* ramp-down */
+ { 25, 25, 31, 16, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 27, 31, 31, 8, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ramp-down */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1800[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 31, 31, 4, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 31, 31, 4, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
+
+struct txcal_ramp_def rf_tx_ramps_1900[RF_TX_RAMP_SIZE] = {
+ { /* profile 0 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 1 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 2 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 3 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 4 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 14, 31, 31, 31, 20, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 5 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 6 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 31, 0, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 7 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 4, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 8 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 29, 31, 31, 31, 5, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 9 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 10 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 24, 31, 31, 31, 10, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 11 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 31, 31, 31, 15, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 12 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 13 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 9, 31, 31, 31, 25, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 14 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ { /* profile 15 */
+ /* ramp-up */
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 31, 31, 31, 0, 0},
+ /* ramp-down */
+ { 31, 31, 31, 31, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+};
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 752628fd..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 */
@@ -58,7 +54,7 @@ enum {
#define L3_MSG_HEAD 4
static uint8_t sim_data[256]; /* buffer for SIM command */
-static volatile uint16_t sim_len = 0; /* lenght of data in sim_data[] */
+static volatile uint16_t sim_len = 0; /* length of data in sim_data[] */
static volatile uint8_t sim_state = SIM_STATE_IDLE;
/* current state of SIM process */
static volatile uint8_t sim_ignore_waiting_char = 0;
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 0b60292a..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>
@@ -75,44 +71,43 @@ static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM;
#ifdef TPU_DEBUG
#include <comm/sercomm.h>
#include <layer1/sync.h>
-static void tpu_ram_read_en(int enable)
-{
- uint16_t reg;
+static struct msgb *tpu_debug_msg = NULL;
- reg = readw(TPU_REG(TPU_CTRL));
- if (enable)
- reg |= TPU_CTRL_MCU_RAM_ACC;
- else
- reg &= ~TPU_CTRL_MCU_RAM_ACC;
- writew(reg, TPU_REG(TPU_CTRL));
+static void tpu_debug_alloc(void)
+{
+ tpu_debug_msg = sercomm_alloc_msgb(sizeof(uint32_t) + 64*2);
+ if (!tpu_debug_msg)
+ printf("UNABLE TO ALLOC TPU DBG\n");
}
-
-static void tpu_debug(void)
+static void tpu_debug_flush(void)
{
- uint16_t *tpu_base = (uint16_t *)BASE_ADDR_TPU_RAM;
- unsigned int tpu_size = tpu_ptr - tpu_base;
- struct msgb *msg = sercomm_alloc_msgb(tpu_size*2);
- uint16_t *data;
- uint32_t *fn;
- uint16_t reg;
- int i;
-
- /* prepend tpu memory dump with frame number */
- fn = (uint32_t *) msgb_put(msg, sizeof(fn));
- *fn = l1s.current_time.fn;
-
- tpu_ram_read_en(1);
-
- data = (uint16_t *) msgb_put(msg, tpu_size*2);
- for (i = 0; i < tpu_size; i ++)
- data[i] = tpu_base[i];
-
- tpu_ram_read_en(0);
-
- sercomm_sendmsg(SC_DLCI_DEBUG, msg);
+ if (tpu_debug_msg) {
+ sercomm_sendmsg(SC_DLCI_DEBUG, tpu_debug_msg);
+ tpu_debug_msg = NULL;
+ }
+ tpu_debug_alloc();
+}
+static void tpu_debug_enqueue(uint16_t instr)
+{
+ uint16_t *u16_out;
+
+ if (!tpu_debug_msg)
+ return;
+ if (tpu_ptr == (uint16_t *) BASE_ADDR_TPU_RAM) {
+ /* prepend tpu memory dump with frame number */
+ uint32_t *fn = (uint32_t *) msgb_put(tpu_debug_msg, sizeof(fn));
+ *fn = l1s.current_time.fn;
+ }
+ if (msgb_tailroom(tpu_debug_msg) >= sizeof(instr)) {
+ /* cannot use msgb_put_u16 as host program expects little endian */
+ u16_out = (uint16_t *) msgb_put(tpu_debug_msg, sizeof(instr));
+ *u16_out = instr;
+ }
}
#else
-static void tpu_debug(void) { }
+static void tpu_debug_alloc(void) { }
+static void tpu_debug_flush(void) { }
+static void tpu_debug_enqueue(uint16_t instr) { }
#endif
#define BIT_SET 1
@@ -167,14 +162,14 @@ void tpu_enable(int active)
printd("tpu_enable(%u)\n", active);
- tpu_debug();
-
if (active)
reg |= TPU_CTRL_EN;
else
reg &= ~TPU_CTRL_EN;
writew(reg, TPU_REG(TPU_CTRL));
+ tpu_debug_flush();
+
/* After the new scenario is loaded, TPU switches the MCU-visible memory
* page, i.e. we can write without any danger */
tpu_rewind();
@@ -241,6 +236,7 @@ void tpu_rewind(void)
void tpu_enqueue(uint16_t instr)
{
printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr);
+ tpu_debug_enqueue(instr);
*tpu_ptr++ = instr;
if (tpu_ptr > (uint16_t *) TPU_RAM_END)
puts("TPU enqueue beyond end of TPU memory\n");
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 3524ba58..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.
- *
*/
@@ -32,8 +28,6 @@
#define NO_TALLOC
-void *tall_msgb_ctx;
-
#ifdef NO_TALLOC
/* This is a poor mans static allocator for msgb objects */
#define MSGB_DATA_SIZE 256+4
diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c
index 490e2254..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>
@@ -182,7 +178,7 @@ int sercomm_drv_pull(uint8_t *ch)
sercomm_unlock(&flags);
return 1;
} else {
- /* no more data avilable */
+ /* no more data available */
sercomm_unlock(&flags);
return 0;
}
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 ffb59d81..95ea2dfc 100644
--- a/src/target/firmware/fb/fb_bw8.c
+++ b/src/target/firmware/fb/fb_bw8.c
@@ -16,12 +16,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public 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 <fb/framebuffer.h>
#include <fb/fb_bw8.h>
@@ -51,7 +48,7 @@ static void fb_bw8_update_damage(
uint16_t x2,uint16_t y2 /* right lower corner (inclusive) */
){
fb_sanitize_box(&x1,&y1,&x2,&y2);
-
+
x2++; /* see definition of fb_bw8->damage_x2/y2 */
y2++;
@@ -86,17 +83,6 @@ static void fb_bw8_update_damage(
#endif
}
-static void fb_bw8_line(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2){
- fb_sanitize_box(&x1,&y1,&x2,&y2);
- /* FIXME : this is currently unimplemented! */
-}
-
-void fb_bw8_lineto(uint16_t x,uint16_t y){
- fb_bw8_line(framebuffer->cursor_x,framebuffer->cursor_y,x,y);
- framebuffer->cursor_x = x;
- framebuffer->cursor_y = y;
-}
-
/* depending on color set (add to or_mask) or clear
(remove from and_mask) bit number bitnum */
static void set_pixel(uint8_t *and_mask,
@@ -166,13 +152,56 @@ fb_bw8_boxto(uint16_t x,uint16_t y){
framebuffer->cursor_y = y;
}
+/* Just set the given pixel to the current front ground color.
+ * This function does not update the damage rectangle! */
+void fb_bw8_set_pixel(uint16_t x,uint16_t y){
+ uint8_t *p = fb_bw8->mem + (y/8)*framebuffer->width + x;
+ uint8_t and_mask = 0xff, or_mask = 0x00;
+ set_fg_pixel(&and_mask, &or_mask, y % 8);
+ *p = (*p & and_mask)|or_mask;
+ /* printf("fb_bw8_set_pixel: set: (%u|%u)\n", x, y); */
+}
+
+/* Copy Paste from
+ * http://de.wikipedia.org/wiki/Bresenham-Algorithmus#Kompakte_Variante */
+static void fb_bw8_line(int16_t x1,int16_t y1,int16_t x2,int16_t y2){
+ fb_limit_fb_range(&x1, &y1);
+ fb_limit_fb_range(&x2, &y2);
+ fb_bw8_update_damage(x1,y1,x2,y2);
+ /* printf("fb_bw8_line from (%u|%u) -> (%u|%u)\n", x1, y1, x2, y2); */
+ int16_t dx = abs(x2-x1), dy = -abs(y2-y1);
+ int16_t sx = x1<x2 ? 1 : -1, sy = y1<y2 ? 1 : -1;
+ int16_t err = dx+dy, e2; /* error value e_xy */
+
+ while (1) {
+ fb_bw8_set_pixel(x1,y1);
+ if (x1==x2 && y1==y2) break;
+ e2 = 2*err;
+ if (e2 > dy) { err += dy; x1 += sx; } /* e_xy+e_x > 0 */
+ if (e2 < dx) { err += dx; y1 += sy; } /* e_xy+e_y < 0 */
+ }
+}
+
+/* Set the given pixel to the current front ground color and update the damage
+ * rectangle. */
+void fb_bw8_set_p(uint16_t x,uint16_t y){
+ fb_bw8_update_damage(x,y,x+1,y+1);
+ fb_bw8_set_pixel(x,y);
+}
+
+void fb_bw8_lineto(uint16_t x,uint16_t y){
+ fb_bw8_line(framebuffer->cursor_x,framebuffer->cursor_y,x,y);
+ framebuffer->cursor_x = x;
+ framebuffer->cursor_y = y;
+}
+
+
/* this is the most ridiculous function ever, because it has to
fiddle with two braindead bitmaps at once, both being
organized differently */
/* draw text at current position, with current font and colours up
to a width of maxwidth pixels, return pixelwidth consumed */
-
int
fb_bw8_putstr(char *str,int maxwidth){
const struct fb_font *font = fb_fonts[framebuffer->font];
@@ -187,7 +216,7 @@ fb_bw8_putstr(char *str,int maxwidth){
int bitmap_offs,bitmap_bit; // offset inside bitmap, bit number of pixel
int fb8_offs; // offset to current pixel in framebuffer
uint8_t and_mask,or_mask; // to draw on framebuffer
- uint8_t *p; // pointer into framebuffer memorya
+ uint8_t *p; // pointer into framebuffer memory
int total_w; // total width
/* center, if maxwidth < 0 */
@@ -251,7 +280,7 @@ fb_bw8_putstr(char *str,int maxwidth){
bitmap_y = fchr->bbox_h -
(char_y - fchr->bbox_y) - 1;
- fb8_offs = framebuffer->cursor_x +
+ fb8_offs = framebuffer->cursor_x +
char_x + (y/8)*framebuffer->width;
and_mask = 0xff;
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 08d64e13..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>
@@ -144,7 +140,7 @@ void fb_rgb332_lineto(uint16_t x2,uint16_t y2){
int t,tmax; /* counter for steps */
int err_inc,err_accu=0; /* error delta and accumulator for */
- /* Brensenham's algorhithm */
+ /* Brensenham's algorithm */
fb_limit_fb_range(&x1,&y1);
fb_limit_fb_range(&x2,&y2);
diff --git a/src/target/firmware/fb/fb_s6b33b1x.c b/src/target/firmware/fb/fb_s6b33b1x.c
index 788ada7b..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>
@@ -72,7 +68,7 @@ s6b33b1x_initdata[] = {
{ DATA, 0x0a }, /* DATA: fPCK = fOSC/6 */
{ CMD, 0x2a }, /* CMD Contrast Control */
{ DATA, 0x2d }, /* DATA: default contrast */
- { CMD, 0x30 }, /* CMD Adressing mode set */
+ { CMD, 0x30 }, /* CMD Addressing mode set */
{ DATA, 0x0b }, /* DATA: 65536 color mode */
{ CMD, 0x10 }, /* CMD Driver output mode set */
{ DATA, 0x03 }, /* DATA: Display duty: 1/132 */
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 fdcd38fb..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>
@@ -118,6 +114,7 @@ static struct framebuffer fb_st7558_framebuffer = {
.clear = fb_bw8_clear,
.boxto = fb_bw8_boxto,
.lineto = fb_bw8_lineto,
+ .set_p = fb_bw8_set_p,
.putstr = fb_bw8_putstr,
.flush = fb_st7558_flush,
.width = ST7558_WIDTH,
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/abb/twl3025.h b/src/target/firmware/include/abb/twl3025.h
index 938a4252..8bc2fb9d 100755
--- a/src/target/firmware/include/abb/twl3025.h
+++ b/src/target/firmware/include/abb/twl3025.h
@@ -122,7 +122,7 @@ enum vrpcsts_reg_bits {
enum togbr2_bits {
TOGBR2_KEEPR = (1 << 0), /* Clear KEEPON bit */
TOGBR2_KEEPS = (1 << 1), /* Set KEEPON bit */
- TOGBR2_ACTR = (1 << 2), /* Dectivate MCLK */
+ TOGBR2_ACTR = (1 << 2), /* Deactivate MCLK */
TOGBR2_ACTS = (1 << 3), /* Activate MCLK */
TOGBR2_IBUFPTR1 = (1 << 4), /* Initialize pointer of burst buffer 1 */
TOGBR2_IBUFPTR2 = (1 << 5), /* Initialize pointer of burst buffer 2 */
@@ -147,6 +147,7 @@ uint16_t twl3025_reg_read(uint8_t reg);
int twl3025_get_pwon(void);
void twl3025_power_off(void);
+void twl3025_power_off_now(void);
void twl3025_clk13m(int enable);
diff --git a/src/target/firmware/include/calypso/dsp_api.h b/src/target/firmware/include/calypso/dsp_api.h
index f9751f37..234964f5 100644
--- a/src/target/firmware/include/calypso/dsp_api.h
+++ b/src/target/firmware/include/calypso/dsp_api.h
@@ -45,7 +45,7 @@ typedef struct
API d_task_ra; // (7) RA task command.
API d_fn; // (8) FN, in Rep. period and FN%104, used for TRAFFIC/TCH only.
// bit [0..7] -> b_fn_report, FN in the normalized reporting period.
- // bit [8..15] -> b_fn_sid, FN % 104, used for SID positionning.
+ // bit [8..15] -> b_fn_sid, FN % 104, used for SID positioning.
API d_ctrl_tch; // (9) Tch channel description.
// bit [0..3] -> b_chan_mode, channel mode.
// bit [4..5] -> b_chan_type, channel type.
@@ -72,7 +72,7 @@ typedef struct
// word 1, bit [0..11] -> T1.
API d_power_ctl; // (14) Power level control.
API d_afc; // (15) AFC value (enabled by "b_afc" in "d_ctrl_TCM4400 or in d_ctrl_abb").
- API d_ctrl_system; // (16) Controle Register for RESET/RESUME.
+ API d_ctrl_system; // (16) Control Register for RESET/RESUME.
// bit [0..2] -> b_tsq, training sequence.
// bit [3] -> b_bcch_freq_ind, BCCH frequency indication.
// bit [15] -> b_task_abort, DSP task abort command.
@@ -264,7 +264,7 @@ T_DB_DSP_TO_MCU;
// Speech Recognition module
API d_sr_status; // status of the DSP speech reco task
- API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API d_sr_param; // parameters for the DSP speech reco task: OOV threshold.
API d_sr_bit_exact_test; // bit exact test
API d_sr_nb_words; // number of words used in the speech recognition task
API d_sr_db_level; // estimate voice level in dB
@@ -608,7 +608,7 @@ T_DB_DSP_TO_MCU;
// Speech Recognition module
API d_sr_status; // status of the DSP speech reco task
- API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API d_sr_param; // parameters for the DSP speech reco task: OOV threshold.
API d_sr_bit_exact_test; // bit exact test
API d_sr_nb_words; // number of words used in the speech recognition task
API d_sr_db_level; // estimate voice level in dB
@@ -766,7 +766,7 @@ T_DB_DSP_TO_MCU;
// The size of this buffer is 15 word but some speech reco words
// are overlayer with this buffer. This is the reason why the size is 3 instead of 15.
API d_sr_status; // status of the DSP speech reco task
- API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API d_sr_param; // parameters for the DSP speech reco task: OOV threshold.
API sr_hole1; // hole
API d_sr_bit_exact_test; // bit exact test
API d_sr_nb_words; // number of words used in the speech recognition task
@@ -989,7 +989,7 @@ typedef struct
// The size of this buffer is 15 word but some speech reco words
// are overlayer with this buffer. This is the reason why the size is 3 instead of 15.
API d_sr_status; // status of the DSP speech reco task
- API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API d_sr_param; // parameters for the DSP speech reco task: OOV threshold.
API sr_hole1; // hole
API d_sr_bit_exact_test; // bit exact test
API d_sr_nb_words; // number of words used in the speech recognition task
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 d4d442cc..476a8a86 100644
--- a/src/target/firmware/include/calypso/l1_environment.h
+++ b/src/target/firmware/include/calypso/l1_environment.h
@@ -90,9 +90,9 @@ typedef signed short API_SIGNED;
// bits in d_gsm_bgd_mgt - background task management
#define B_DSPBGD_RECO 1 // start of reco in dsp background
-#define B_DSPBGD_UPD 2 // start of alignement update in dsp background
+#define B_DSPBGD_UPD 2 // start of alignment update in dsp background
#define B_DSPBGD_STOP_RECO 256 // stop of reco in dsp background
-#define B_DSPBGD_STOP_UPD 512 // stop of alignement update in dsp background
+#define B_DSPBGD_STOP_UPD 512 // stop of alignment update in dsp background
// bit in d_pll_config
#define B_32KHZ_CALIB (1 << 14) // force DSP in Idle1 during 32 khz calibration
@@ -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 5e33bdbd..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
@@ -160,7 +156,7 @@ void calypso_sim_powerdown(void); /* Powerdown simcard */
/* APDU transmission modes */
#define SIM_APDU_PUT 0 /* Transmit a data body to the card */
-#define SIM_APDU_GET 1 /* Fetch data from the card eg. GET RESOPNSE */
+#define SIM_APDU_GET 1 /* Fetch data from the card eg. GET RESPONSE */
void calypso_sim_init(void); /* Initialize simcard interface */
diff --git a/src/target/firmware/include/comm/timer.h b/src/target/firmware/include/comm/timer.h
index 1996f666..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
@@ -61,7 +57,7 @@ extern unsigned long volatile jiffies;
* timer management
*/
void osmo_timer_add(struct osmo_timer_list *timer);
-void osmo_timer_schedule(struct osmo_timer_list *timer, int miliseconds);
+void osmo_timer_schedule(struct osmo_timer_list *timer, int milliseconds);
void osmo_timer_del(struct osmo_timer_list *timer);
int osmo_timer_pending(struct osmo_timer_list *timer);
diff --git a/src/target/firmware/include/fb/fb_bw8.h b/src/target/firmware/include/fb/fb_bw8.h
index c77fa71f..db0b31a5 100644
--- a/src/target/firmware/include/fb/fb_bw8.h
+++ b/src/target/firmware/include/fb/fb_bw8.h
@@ -3,12 +3,12 @@
/* 8bit monochrome framebuffer, organized with 8 stacked pixels
per byte, backed by local memory. fb_bw8.c lists functions that
- are common to simmilar organized displays. */
+ are common to similar organized displays. */
/*
- Sketch of Memory Layout
+ Sketch of Memory Layout
Left Upper Corner of Display
-
+
col0 col2
col1
+-------------
@@ -22,7 +22,7 @@
...
Backing store (and internal display memory?) looks like...
-
+
uint8_t mem[] = { A, B, C, .... Q, R, S, ... }
We work on a in-memory copy of the framebuffer and only
@@ -45,6 +45,7 @@ extern struct fb_bw8 *fb_bw8; /* symbol defined by the specific LCD driver */
extern void fb_bw8_clear();
extern void fb_bw8_boxto(uint16_t x,uint16_t y); /* draw a box from cursor to x,y */
extern void fb_bw8_lineto(uint16_t x,uint16_t y); /* draw a line from cursor to x,y */
+extern void fb_bw8_set_p(uint16_t x,uint16_t y);
extern int fb_bw8_putstr(char *str,int maxwidth);
diff --git a/src/target/firmware/include/fb/framebuffer.h b/src/target/firmware/include/fb/framebuffer.h
index e765b36d..8a565659 100644
--- a/src/target/firmware/include/fb/framebuffer.h
+++ b/src/target/firmware/include/fb/framebuffer.h
@@ -8,7 +8,7 @@
/* if a color is "special", then the RGB components most likely
don't make sense. Use "special" colours when you have to
mask out bits with transparency or you have to encode
- colours in a fixed color palette... */
+ colours in a fixed color palette ... */
#define FB_COLOR_WHITE 0x00ffffffU
#define FB_COLOR_BLACK 0x00000000U
@@ -31,6 +31,7 @@ struct framebuffer {
char name[8]; // keep it short!
void (*init)(); // (re)initialize
void (*clear)(); // clear display
+ void (*set_p)(uint16_t x,uint16_t y); // set pixel to fg color
void (*boxto)(uint16_t x,uint16_t y); // draw box to xy
void (*lineto)(uint16_t x,uint16_t y); // draw line to xy
int (*putstr)(char *c,int maxwidth); // put text in current font to fb
@@ -66,6 +67,11 @@ fb_lineto(uint16_t x,uint16_t y){
framebuffer->lineto(x,y);
}
+static inline void
+fb_set_p(uint16_t x,uint16_t y){
+ framebuffer->set_p(x,y);
+}
+
static inline int
fb_putstr(char *str,int maxwidth){
return framebuffer->putstr(str,maxwidth);
diff --git a/src/target/firmware/include/layer1/apc.h b/src/target/firmware/include/layer1/apc.h
deleted file mode 100644
index 3d73c23e..00000000
--- a/src/target/firmware/include/layer1/apc.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef _L1_APC_H
-#define _L1_APC_H
-
-/* determine the AUXAPC value by the Tx Power Level */
-int16_t apc_tx_dbm2auxapc(enum gsm_band band, int8_t dbm);
-
-/* determine the AUXAPC value by the Tx Power Level */
-int16_t apc_tx_pwrlvl2auxapc(enum gsm_band band, uint8_t lvl);
-
-#endif
diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h
index de996a67..b383d477 100644
--- a/src/target/firmware/include/layer1/async.h
+++ b/src/target/firmware/include/layer1/async.h
@@ -32,12 +32,6 @@ int l1a_txq_msgb_count(struct llist_head *queue);
/* flush all pending msgb */
void l1a_txq_msgb_flush(struct llist_head *queue);
-/* request a RACH */
-void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra);
-
-/* schedule frequency change */
-void l1a_freq_req(uint32_t fn_sched);
-
/* Enable a repeating multiframe task */
void l1a_mftask_enable(enum mframe_task task);
@@ -53,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/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h
index 74e2d271..9434fc69 100644
--- a/src/target/firmware/include/layer1/mframe_sched.h
+++ b/src/target/firmware/include/layer1/mframe_sched.h
@@ -31,6 +31,9 @@ enum mframe_task {
MF_TASK_TCH_H_0,
MF_TASK_TCH_H_1,
+ MF_TASK_GPRS_PDTCH,
+ MF_TASK_GPRS_PTCCH,
+
MF_TASK_NEIGH_PM51_C0T0,
MF_TASK_NEIGH_PM51,
MF_TASK_NEIGH_PM26E,
@@ -42,6 +45,7 @@ enum mframe_task {
enum mf_sched_item_flag {
MF_F_SACCH = (1 << 0),
+ MF_F_PTCCH = (1 << 1),
};
/* The scheduler itself */
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/rfch.h b/src/target/firmware/include/layer1/rfch.h
index 344523c3..0c4f9a0f 100644
--- a/src/target/firmware/include/layer1/rfch.h
+++ b/src/target/firmware/include/layer1/rfch.h
@@ -3,7 +3,7 @@
struct gsm_time;
-void rfch_get_params(struct gsm_time *t,
+void rfch_get_params(const struct gsm_time *t,
uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p);
#endif /* _L1_RFCH_H */
diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h
index 3565ee20..e0a4412f 100644
--- a/src/target/firmware/include/layer1/sync.h
+++ b/src/target/firmware/include/layer1/sync.h
@@ -15,7 +15,7 @@ struct l1_cell_info {
uint8_t bsic;
/* Combined or non-combined CCCH */
uint8_t ccch_mode; /* enum ccch_mode */
- /* whats the delta of the cells current GSM frame number
+ /* what's the delta of the cells current GSM frame number
* compared to our current local frame number */
int32_t fn_offset;
/* how much does the TPU need adjustment (delta) to synchronize
@@ -82,6 +82,10 @@ 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;
/* Transmit queues of pending packets for main DCCH and ACCH */
struct llist_head tx_queue[_NUM_L1S_CHAN];
@@ -113,6 +117,7 @@ struct l1s_state {
struct {
uint8_t ra;
+ uint8_t uic;
} rach;
struct {
@@ -124,6 +129,7 @@ struct l1s_state {
GSM_DCHAN_SDCCH_8_CBCH,
GSM_DCHAN_TCH_H,
GSM_DCHAN_TCH_F,
+ GSM_DCHAN_PDCH,
GSM_DCHAN_UNKNOWN,
} type;
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/include/rf/readcal.h b/src/target/firmware/include/rf/readcal.h
new file mode 100644
index 00000000..1f16bdac
--- /dev/null
+++ b/src/target/firmware/include/rf/readcal.h
@@ -0,0 +1 @@
+void read_factory_rf_calibration(void);
diff --git a/src/target/firmware/include/rf/txcal.h b/src/target/firmware/include/rf/txcal.h
new file mode 100644
index 00000000..10319424
--- /dev/null
+++ b/src/target/firmware/include/rf/txcal.h
@@ -0,0 +1,49 @@
+/*
+ * RF Tx calibration structures matching those used by official
+ * TI/FreeCalypso firmwares; these structures appear in the flash file
+ * system (FFS) of FCDEV3B and GTA0x devices and in the factory data
+ * block on the Pirelli DP-L10 phone.
+ */
+
+#define RF_TX_CHAN_CAL_TABLE_SIZE 4 /*!< channel calibration table size */
+#define RF_TX_NUM_SUB_BANDS 8 /*!< number of sub-bands in channel calibration table */
+#define RF_TX_LEVELS_TABLE_SIZE 32 /*!< level table size */
+#define RF_TX_RAMP_SIZE 16 /*!< number of ramp definitions */
+
+/* APC of Tx Power (pcm-file "rf/tx/level.gsm|dcs") */
+struct txcal_tx_level {
+ uint16_t apc; /*!< 0..31 */
+ uint8_t ramp_index; /*!< 0..RF_TX_RAMP_SIZE */
+ uint8_t chan_cal_index; /*!< 0..RF_TX_CHAN_CAL_TABLE_SIZE */
+};
+
+/* Power ramp definition structure */
+struct txcal_ramp_def {
+ uint8_t ramp_up[16]; /*!< Ramp-up profile */
+ uint8_t ramp_down[16]; /*!< Ramp-down profile */
+};
+
+/* Tx channel calibration structure */
+struct txcal_chan_cal {
+ uint16_t arfcn_limit;
+ int16_t chan_cal;
+};
+
+extern struct txcal_tx_level rf_tx_levels_850[RF_TX_LEVELS_TABLE_SIZE];
+extern struct txcal_tx_level rf_tx_levels_900[RF_TX_LEVELS_TABLE_SIZE];
+extern struct txcal_tx_level rf_tx_levels_1800[RF_TX_LEVELS_TABLE_SIZE];
+extern struct txcal_tx_level rf_tx_levels_1900[RF_TX_LEVELS_TABLE_SIZE];
+
+extern struct txcal_ramp_def rf_tx_ramps_850[RF_TX_RAMP_SIZE];
+extern struct txcal_ramp_def rf_tx_ramps_900[RF_TX_RAMP_SIZE];
+extern struct txcal_ramp_def rf_tx_ramps_1800[RF_TX_RAMP_SIZE];
+extern struct txcal_ramp_def rf_tx_ramps_1900[RF_TX_RAMP_SIZE];
+
+extern struct txcal_chan_cal rf_tx_chan_cal_850[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS];
+extern struct txcal_chan_cal rf_tx_chan_cal_900[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS];
+extern struct txcal_chan_cal rf_tx_chan_cal_1800[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS];
+extern struct txcal_chan_cal rf_tx_chan_cal_1900[RF_TX_CHAN_CAL_TABLE_SIZE]
+ [RF_TX_NUM_SUB_BANDS];
diff --git a/src/target/firmware/include/rf/vcxocal.h b/src/target/firmware/include/rf/vcxocal.h
new file mode 100644
index 00000000..8c495954
--- /dev/null
+++ b/src/target/firmware/include/rf/vcxocal.h
@@ -0,0 +1,13 @@
+/*
+ * Old OsmocomBB fw code used the same fixed and hard-coded numbers for
+ * the slope of the VCXO and the initial AFC DAC setting for all Calypso
+ * targets, but this approach is incorrect because different phone/modem
+ * board designs use different VC(TC)XO components with different properties,
+ * and some manufacturers have done per-unit calibration of their VC(TC)XO.
+ *
+ * We now have global variables in which these configuration or calibration
+ * values are stored, and this header file provides the extern definitions
+ * for these global vars.
+ */
+
+extern int16_t afc_initial_dac_value, afc_slope;
diff --git a/src/target/firmware/include/stdint.h b/src/target/firmware/include/stdint.h
index 627403f9..5997f43b 100644
--- a/src/target/firmware/include/stdint.h
+++ b/src/target/firmware/include/stdint.h
@@ -19,7 +19,7 @@
#include_next <stdint.h>
#endif
-#ifndef __int8_t_defined
+#if (!defined(__int8_t_defined) && !defined(INT8_MAX))
typedef signed char int8_t;
typedef unsigned char uint8_t;
diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile
index 0c710a5d..0a24e054 100644
--- a/src/target/firmware/layer1/Makefile
+++ b/src/target/firmware/layer1/Makefile
@@ -2,7 +2,7 @@
LIBRARIES+=layer1
LIB_layer1_DIR=layer1
LIB_layer1_SRCS=avg.c agc.c afc.c toa.c sync.c tdma_sched.c tpu_window.c init.c \
- l23_api.c mframe_sched.c sched_gsmtime.c async.c rfch.c apc.c
+ l23_api.c mframe_sched.c sched_gsmtime.c async.c rfch.c
LIB_layer1_SRCS += prim_pm.c prim_rach.c prim_tx_nb.c prim_rx_nb.c prim_fbsb.c \
prim_freq.c prim_utils.c prim_tch.c
diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c
index a51a1071..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>
@@ -30,7 +26,7 @@
#include <layer1/avg.h>
#include <calypso/dsp.h>
-#define AFC_INITIAL_DAC_VALUE -700
+#include <rf/vcxocal.h>
/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */
#define AFC_PERIOD 40
@@ -53,7 +49,6 @@ static struct afc_state afc_state = {
.period = AFC_PERIOD,
.min_valid = AFC_MIN_MUN_VALID,
},
- .dac_value = AFC_INITIAL_DAC_VALUE,
};
/* The AFC DAC in the ABB has to be configured as follows:
@@ -66,10 +61,6 @@ static struct afc_state afc_state = {
#define AFC_NORM_FACTOR_GSM ((1<<15) / 947)
#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894)
-/* we assume 8.769ppb per LSB, equals 0.008769 * 32768 == 287 */
-//#define AFC_SLOPE 320
-#define AFC_SLOPE 287
-
/* The DSP can measure the frequency error in the following ranges:
* FB_MODE0: +/- 20 kHz
* FB_MODE1: +/- 4 kHz
@@ -92,7 +83,7 @@ void afc_correct(int16_t freq_error, uint16_t arfcn)
afc_norm_factor = AFC_NORM_FACTOR_DCS;
}
- delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE);
+ delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / afc_slope);
printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n",
freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta);
afc_state.dac_value += delta;
@@ -106,7 +97,7 @@ void afc_correct(int16_t freq_error, uint16_t arfcn)
void afc_reset(void)
{
- afc_state.dac_value = AFC_INITIAL_DAC_VALUE;
+ afc_state.dac_value = afc_initial_dac_value;
}
void afc_input(int32_t freq_error, uint16_t arfcn, int valid)
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/apc.c b/src/target/firmware/layer1/apc.c
deleted file mode 100644
index 480c7607..00000000
--- a/src/target/firmware/layer1/apc.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/* APC (Automatic Power Control) Implementation */
-
-/* (C) 2010 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 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 <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <layer1/apc.h>
-
-/* calibration table defined in board file */
-extern const int16_t dbm2apc_gsm900[];
-extern const int dbm2apc_gsm900_max;
-
-
-/* determine the AUXAPC value by the Tx Power Level */
-int16_t apc_tx_dbm2auxapc(enum gsm_band band, int8_t dbm)
-{
- if (dbm < 0)
- return -ERANGE;
-
- /* FIXME: offsets for different bands! */
- if (dbm > dbm2apc_gsm900_max)
- dbm = dbm2apc_gsm900_max;
-
- return dbm2apc_gsm900[dbm];
-}
-
-/* determine the AUXAPC value by the Tx Power Level */
-int16_t apc_tx_pwrlvl2auxapc(enum gsm_band band, uint8_t lvl)
-{
- /* convert tx power level to dBm */
- int dbm = ms_pwr_dbm(band, lvl);
- if (dbm < 0)
- return dbm;
-
- return apc_tx_dbm2auxapc(band, dbm);
-}
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 e78020de..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,59 +73,76 @@ 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 = 0;
- uint32_t neigh_task = 0;
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 */
+ 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);
}
+
switch (neigh_mode) {
case NEIGH_MODE_PM:
switch (multiframe) {
case MF51:
- neigh_task = (1 << MF_TASK_NEIGH_PM51);
+ TASK_SET(MF_TASK_NEIGH_PM51);
break;
case MF26EVEN:
- neigh_task = (1 << MF_TASK_NEIGH_PM26E);
+ TASK_SET(MF_TASK_NEIGH_PM26E);
break;
case MF26ODD:
- neigh_task = (1 << MF_TASK_NEIGH_PM26O);
+ TASK_SET(MF_TASK_NEIGH_PM26O);
+ break;
+ default:
+ /* no neighbor measurement */
break;
}
break;
}
- return (1 << master_task) | neigh_task;
+
+#undef TASK_SET
+
+ return task_mask;
}
static int chan_nr2dchan_type(uint8_t chan_nr)
@@ -144,6 +157,9 @@ static int chan_nr2dchan_type(uint8_t chan_nr)
return GSM_DCHAN_SDCCH_4;
} else if ((cbits & 0x18) == 0x08) {
return GSM_DCHAN_SDCCH_8;
+ } else if ((cbits & 0x1f) == 0x18) {
+ /* Osmocom-specific extension for PDCH */
+ return GSM_DCHAN_PDCH;
} else if ((cbits & 0x1f) == 0x19) {
/* Osmocom-specific extension for CBCH on SDCCH/4 */
return GSM_DCHAN_SDCCH_4_CBCH;
@@ -241,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);
@@ -260,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 */
@@ -269,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 */
@@ -330,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;
@@ -342,6 +361,7 @@ static void l1ctl_rx_dm_rel_req(struct msgb *msg)
dsp_load_ciph_param(0, NULL);
l1a_tch_mode_set(GSM48_CMODE_SIGN);
audio_set_enabled(GSM48_CMODE_SIGN, 0);
+ l1s.tch_loop_mode = L1CTL_TCH_LOOP_OPEN;
l1s.neigh_pm.n = 0;
}
@@ -366,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 */
@@ -503,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;
@@ -511,6 +530,8 @@ 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);
}
@@ -523,17 +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 */
@@ -570,7 +596,7 @@ static void l1ctl_rx_traffic_req(struct msgb *msg)
struct l1ctl_traffic_req *tr = (struct l1ctl_traffic_req *) ul->payload;
int num = 0;
- /* printd("L1CTL_TRAFFIC_REQ\n"); */ /* Very verbose, can overwelm serial */
+ /* printd("L1CTL_TRAFFIC_REQ\n"); */ /* Very verbose, can overwhelm serial */
msg->l2h = tr->data;
@@ -705,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 9c2954c3..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>
@@ -311,6 +307,52 @@ static const struct mframe_sched_item mf_neigh_pm26_odd[] = {
{ .sched_set = NULL }
};
+/* See 3GPP TS 45.002, table 6 */
+static const struct mframe_sched_item mf_gprs_pdtch[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 13, .frame_nr = 0 },
+ { .sched_set = NB_QUAD_DL, .modulo = 13, .frame_nr = 4 },
+ { .sched_set = NB_QUAD_DL, .modulo = 13, .frame_nr = 8 },
+ /* NOTE: receive only task */
+ { .sched_set = NULL }
+};
+
+static const struct mframe_sched_item mf_gprs_ptcch[] = {
+ /* TODO: implement AB_PTCCH_UL for PTCCH/U */
+ /* TODO: implement NB_PTCCH_DL for PTCCH/D */
+#if 0
+ /* PTCCH/D */
+ { .sched_set = NB_PTCCH_DL, .modulo = 104, .frame_nr = 12, .flags = MF_F_PTCCH },
+ { .sched_set = NB_PTCCH_DL, .modulo = 104, .frame_nr = 38, .flags = MF_F_PTCCH },
+ { .sched_set = NB_PTCCH_DL, .modulo = 104, .frame_nr = 64, .flags = MF_F_PTCCH },
+ { .sched_set = NB_PTCCH_DL, .modulo = 104, .frame_nr = 90, .flags = MF_F_PTCCH },
+
+ /* PTCCH/U for TAI 0 .. 3 */
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 12 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 38 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 64 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 90 },
+
+ /* PTCCH/U for TAI 4 .. 7 */
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 116 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 142 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 168 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 194 },
+
+ /* PTCCH/U for TAI 8 .. 11 */
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 220 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 246 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 272 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 298 },
+
+ /* PTCCH/U for TAI 12 .. 15 */
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 324 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 350 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 376 },
+ { .sched_set = AB_PTCCH_UL, .modulo = 416, .frame_nr = 402 },
+#endif
+ { .sched_set = NULL }
+};
+
/* Test TX */
static const struct mframe_sched_item mf_tx_all_nb[] = {
{ .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 },
@@ -345,6 +387,9 @@ static const struct mframe_sched_item *sched_set_for_task[32] = {
[MF_TASK_TCH_H_0] = mf_tch_h_0,
[MF_TASK_TCH_H_1] = mf_tch_h_1,
+ [MF_TASK_GPRS_PDTCH] = mf_gprs_pdtch,
+ [MF_TASK_GPRS_PTCCH] = mf_gprs_ptcch,
+
[MF_TASK_NEIGH_PM51_C0T0] = mf_neigh_pm51_c0t0,
[MF_TASK_NEIGH_PM51] = mf_neigh_pm51,
[MF_TASK_NEIGH_PM26E] = mf_neigh_pm26_even,
@@ -415,6 +460,10 @@ uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts)
break;
/* Osmocom specific extensions */
+ case MF_TASK_GPRS_PDTCH:
+ case MF_TASK_GPRS_PTCCH:
+ cbits = 0x18;
+ break;
case MF_TASK_SDCCH4_CBCH:
cbits = 0x19;
break;
diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c
index 50acefcc..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>
@@ -234,7 +230,7 @@ static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt,
synchronize_tdma(&l1s.serving_cell);
- /* if we have recived a SYNC burst, update our local GSM time */
+ /* if we have received a SYNC burst, update our local GSM time */
gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY);
/* compute next time from new current time */
l1s.next_time = l1s.current_time;
@@ -532,7 +528,7 @@ static void l1a_fb_compl(__unused enum l1_compl c)
return;
}
- /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */
+ /* FIXME: use l1s.neigh_cell[fbs.cinfo_idx] */
fbinfo2cellinfo(&l1s.serving_cell, last_fb);
/* send FBSB_CONF success message via L1CTL */
diff --git a/src/target/firmware/layer1/prim_freq.c b/src/target/firmware/layer1/prim_freq.c
index 01f39d48..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>
@@ -49,11 +45,6 @@
#include <l1ctl_proto.h>
-struct {
- uint32_t fn;
- uint16_t band_arfcn;
-} last_rach;
-
/* if the "starting time" is reached, use frequencies "after time" */
static int l1s_freq_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3)
{
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 38c7b53b..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,6 +122,8 @@ 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;
+ 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 a8036d2f..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:
@@ -526,7 +514,7 @@ skip_tx_traffic:
dsp_load_tch_param(
&l1s.next_time,
tch_mode, tch_f_hn ? TCH_F : TCH_H, tch_sub,
- 0, sync, tn
+ l1s.tch_loop_mode, sync, tn
);
dsp_load_rx_task(
@@ -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 */
@@ -585,7 +579,7 @@ static int l1s_tch_d_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
dsp_load_tch_param(
&l1s.next_time,
tch_mode, tch_f_hn ? TCH_F : TCH_H, tch_sub,
- 0, 0, tn
+ l1s.tch_loop_mode, 0, tn
);
dsp_load_rx_task(TCHD_DSP_TASK, 0, tsc); /* burst_id unused for TCH */
@@ -607,7 +601,7 @@ const struct tdma_sched_item tch_d_sched_set[] = {
* ------------------------------------------------------------------------- */
/*
- * SACCH data are spread over 4 bursts, however they are so far appart that
+ * SACCH data are spread over 4 bursts, however they are so far apart that
* we can't use the normal scheduler to schedule all them at once in a single
* set.
* Therefore, the task code itself decides in which burst it is, if it's the
@@ -791,7 +785,7 @@ static int l1s_tch_a_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3)
dsp_load_tch_param(
&l1s.next_time,
tch_mode, tch_f_hn ? TCH_F : TCH_H, tch_sub,
- 0, 0, tn
+ l1s.tch_loop_mode, 0, tn
);
dsp_load_rx_task(
@@ -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 c85da717..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>
@@ -43,10 +39,22 @@ static uint8_t ubMeas[23] = {
/* lapdm header */
0x01, 0x03, 0x49,
- /* Measurement report */
- 0x06, 0x15, 0x36, 0x36, 0x01, 0xC0, 0x00, 0x00,
+ /* 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, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00
};
diff --git a/src/target/firmware/layer1/rfch.c b/src/target/firmware/layer1/rfch.c
index d0818d04..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>
@@ -82,9 +78,16 @@ static int pow_nbin_mask(int n)
return x;
}
-static int16_t rfch_hop_seq_gen(struct gsm_time *t,
+/*! Hopping sequence generator.
+ * \param[in] t GSM time (frame number)
+ * \param[in] hsn Hopping Sequence Number
+ * \param[in] maio Mobile Allocation Index OFfset
+ * \param[in] n number of entries in mobile allocation (arfcn table)
+ * \param[in] arfcn_tbl Array of ARFCN representing the mobile allocation
+ * \returns ARFCN to use for given input parameters at time 't' or MAI if arfcn_tbl == NULL */
+static int16_t rfch_hop_seq_gen(const struct gsm_time *t,
uint8_t hsn, uint8_t maio,
- uint8_t n, uint16_t *arfcn_tbl)
+ uint8_t n, const uint16_t *arfcn_tbl)
{
int mai;
@@ -114,8 +117,12 @@ static int16_t rfch_hop_seq_gen(struct gsm_time *t,
}
-/* RF Channel parameters */
-void rfch_get_params(struct gsm_time *t,
+/*! Get RF Channel parameters at a given GSM time (frame number)
+ * \param[in] t GSM time for which to obtain parameters
+ * \param[out] arfcn_p ARFCN to use at time t
+ * \oaram[out] tsc_p Training sequence to use at time t
+ * \param[out] tn_p Timeslot Number to use at time t */
+void rfch_get_params(const struct gsm_time *t,
uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p)
{
if (l1s.dedicated.type == GSM_DCHAN_NONE) {
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 36f42975..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>
@@ -50,13 +46,14 @@
#include <layer1/sync.h>
#include <layer1/afc.h>
#include <layer1/agc.h>
-#include <layer1/apc.h>
#include <layer1/tdma_sched.h>
#include <layer1/mframe_sched.h>
#include <layer1/sched_gsmtime.h>
#include <layer1/tpu_window.h>
#include <layer1/l23_api.h>
+#include <rf/txcal.h>
+
#include <l1ctl_proto.h>
struct l1s_state l1s;
@@ -168,7 +165,7 @@ void l1s_reset_hw(void)
dsp_api.ndb->d_dsp_page = 0;
/* we have to really reset the TPU, otherwise FB detection
- * somtimes returns wrong TOA values. */
+ * sometimes returns wrong TOA values. */
tpu_reset(1);
tpu_reset(0);
tpu_rewind();
@@ -322,19 +319,62 @@ void l1s_dsp_abort(void)
tdma_schedule(0, &l1s_abort_cmd, 0, 0, 0, 10);
}
+/* APC offset is different from different targets */
+extern uint8_t apc_offset; /* see board/.../rf_tables.c */
+
void l1s_tx_apc_helper(uint16_t arfcn)
{
+ struct txcal_chan_cal (*my_chan_cal)[RF_TX_NUM_SUB_BANDS];
+ struct txcal_chan_cal (*chan_cal)[RF_TX_NUM_SUB_BANDS];
+ struct txcal_tx_level *levels, *mylevel;
+ struct txcal_ramp_def *ramps, *myramp;
int16_t auxapc;
- enum gsm_band band;
int i;
- /* Get DAC setting */
- band = gsm_arfcn2band(arfcn);
- auxapc = apc_tx_pwrlvl2auxapc(band, l1s.tx_power);
+ /* Figure out which band we are working in */
+ switch (gsm_arfcn2band(arfcn)) {
+ case GSM_BAND_850:
+ levels = rf_tx_levels_850;
+ ramps = rf_tx_ramps_850;
+ chan_cal = rf_tx_chan_cal_850;
+ break;
+ case GSM_BAND_900:
+ levels = rf_tx_levels_900;
+ ramps = rf_tx_ramps_900;
+ chan_cal = rf_tx_chan_cal_900;
+ break;
+ case GSM_BAND_1800:
+ levels = rf_tx_levels_1800;
+ ramps = rf_tx_ramps_1800;
+ chan_cal = rf_tx_chan_cal_1800;
+ break;
+ case GSM_BAND_1900:
+ levels = rf_tx_levels_1900;
+ ramps = rf_tx_ramps_1900;
+ chan_cal = rf_tx_chan_cal_1900;
+ break;
+ default:
+ puts("Error: invalid band in l1s_tx_apc_helper()!\n");
+ return;
+ }
+
+ /* Figure out our Tx power level, APC and ramp index */
+ mylevel = levels + (l1s.tx_power & 0x1F);
+ auxapc = mylevel->apc;
+ myramp = ramps + mylevel->ramp_index;
+ my_chan_cal = chan_cal + mylevel->chan_cal_index;
+
+ /* Channel calibration correction */
+ arfcn &= ~ARFCN_FLAG_MASK;
+ for (i = 0; i < RF_TX_NUM_SUB_BANDS; i++) {
+ if (arfcn <= (*my_chan_cal)[i].arfcn_limit)
+ break;
+ }
+ if (i < RF_TX_NUM_SUB_BANDS)
+ auxapc = ((uint32_t)auxapc * (*my_chan_cal)[i].chan_cal) / 128;
- /* Load the ApcOffset into the DSP */
- #define MY_OFFSET 4
- dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | MY_OFFSET) | 1; /* 2x slope for the GTA-02 ramp */
+ /* Load the (target specific) ApcOffset into the DSP */
+ dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | apc_offset) | 1; /* 2x slope for the GTA-02 ramp */
/* Load the TX Power into the DSP */
/*
@@ -346,7 +386,8 @@ void l1s_tx_apc_helper(uint16_t arfcn)
/* Update the ramp according to the PCL */
for (i = 0; i < 16; i++)
- dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, twl3025_default_ramp[i]);
+ dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM,
+ ABB_RAMP_VAL(myramp->ramp_up[i], myramp->ramp_down[i]));
/* The Ramp Table is sent to ABB only once after RF init routine called */
dsp_api.db_w->d_ctrl_abb |= (1 << B_RAMP) | (1 << B_BULRAMPDEL);
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/lib/vsprintf.c b/src/target/firmware/lib/vsprintf.c
index 80e8c1ad..8e92e2a2 100644
--- a/src/target/firmware/lib/vsprintf.c
+++ b/src/target/firmware/lib/vsprintf.c
@@ -615,7 +615,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
while(*fmt && *str) {
/* skip any white space in format */
- /* white space in format matchs any amount of
+ /* white space in format matches any amount of
* white space, including none, in the input.
*/
if (isspace(*fmt)) {
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 9de2cc8e..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>
@@ -300,8 +296,8 @@ static void trf6151_pll_tx(uint32_t freq_khz,
/* Low band. We have 3 possible PLL modes that output on
* the right port: GSM900, GSM850_HIGH, GSM850_LOW.
*
- * The transistion points have been chosen looking at the VCO
- * and IF frequencies for various frequencies for theses modes
+ * The transition points have been chosen looking at the VCO
+ * and IF frequencies for various frequencies for these modes
*/
if (freq_khz < 837100) {
/* GSM850_LOW */
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 f2a47c54..531d8dcf 100644
--- a/src/target/trx_toolkit/app_common.py
+++ b/src/target/trx_toolkit/app_common.py
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
# Common helpers for applications
#
-# (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
@@ -17,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
@@ -29,6 +24,9 @@ class ApplicationBase:
# Example: [DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
LOG_FMT_DEFAULT = "[%(levelname)s] %(filename)s:%(lineno)d %(message)s"
+ # Default time / date format (e.g. 2003-01-23 00:29:50)
+ LOG_TIME_FMT_DEFAULT = "%Y-%m-%d %H:%M:%S"
+
def app_print_copyright(self, holders = []):
# Print copyright holders if any
for date, author in holders:
@@ -40,19 +38,27 @@ class ApplicationBase:
"This is free software: you are free to change and redistribute it.\n" \
"There is NO WARRANTY, to the extent permitted by law.\n")
+ def add_log_handler(self, lh, log_level, log_fmt, time_fmt, log_time = False):
+ log_fmt = "%(asctime)s " + log_fmt if log_time else log_fmt
+ lf = log.Formatter(log_fmt, time_fmt)
+ ll = log.getLevelName(log_level)
+
+ log.root.addHandler(lh)
+ lh.setFormatter(lf)
+ lh.setLevel(ll)
+
def app_init_logging(self, argv):
# Default logging handler (stderr)
- sh = log.StreamHandler()
- sh.setLevel(log.getLevelName(argv.log_level))
- sh.setFormatter(log.Formatter(argv.log_fmt))
- log.root.addHandler(sh)
+ lo = (argv.log_level, argv.log_fmt, argv.log_time_fmt, argv.log_time)
+ lh = log.StreamHandler()
+ self.add_log_handler(lh, *lo)
# Optional file handler
if argv.log_file_name is not None:
- fh = log.FileHandler(argv.log_file_name)
- fh.setLevel(log.getLevelName(argv.log_file_level))
- fh.setFormatter(log.Formatter(argv.log_file_fmt))
- log.root.addHandler(fh)
+ lo = (argv.log_file_level, argv.log_file_fmt,
+ argv.log_file_time_fmt, argv.log_file_time)
+ lh = log.FileHandler(argv.log_file_name)
+ self.add_log_handler(lh, *lo)
# Set DEBUG for the root logger
log.root.setLevel(log.DEBUG)
@@ -62,6 +68,13 @@ class ApplicationBase:
dest = "log_level", type = str, default = "DEBUG",
choices = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
help = "Set logging level (default %(default)s)")
+ parser.add_argument("--log-time",
+ dest = "log_time", action = "store_true",
+ help = "Prefix each log message with the current time")
+ parser.add_argument("--log-time-format", metavar = "FMT",
+ dest = "log_time_fmt", type = str,
+ default = self.LOG_TIME_FMT_DEFAULT,
+ help = "Set time format (default %(default)s)")
parser.add_argument("--log-format", metavar = "FMT",
dest = "log_fmt", type = str, default = self.LOG_FMT_DEFAULT,
help = "Set logging message format")
@@ -73,6 +86,13 @@ class ApplicationBase:
dest = "log_file_level", type = str, default = "DEBUG",
choices = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
help = "Set logging level for file (default %(default)s)")
+ parser.add_argument("--log-file-time",
+ dest = "log_file_time", action = "store_true",
+ help = "Prefix each log message with the current time")
+ parser.add_argument("--log-file-time-format", metavar = "FMT",
+ dest = "log_file_time_fmt", type = str,
+ default = self.LOG_TIME_FMT_DEFAULT,
+ help = "Set time format for file (default %(default)s)")
parser.add_argument("--log-file-format", metavar = "FMT",
dest = "log_file_fmt", type = str, default = self.LOG_FMT_DEFAULT,
help = "Set logging message format for file")
diff --git a/src/target/trx_toolkit/burst_fwd.py b/src/target/trx_toolkit/burst_fwd.py
index b418aef1..69245317 100644
--- a/src/target/trx_toolkit/burst_fwd.py
+++ b/src/target/trx_toolkit/burst_fwd.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
# Burst forwarding between transceivers
#
-# (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
#
# All Rights Reserved
#
@@ -17,14 +17,12 @@
# 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
-class BurstForwarder:
+from trx_list import TRXList
+
+class BurstForwarder(TRXList):
""" Performs burst forwarding between transceivers.
BurstForwarder distributes bursts between the list of given
@@ -35,41 +33,22 @@ class BurstForwarder:
- actual RX / TX frequencies,
- list of active timeslots.
- Each to be distributed L12TRX message is being transformed
- into a TRX2L1 message, and then forwarded to transceivers
+ Each to be distributed 'TxMsg' message is being transformed
+ into a 'RxMsg' message, and then forwarded to transceivers
with partially initialized header. All uninitialized header
fields (such as rssi and toa256) shall be set by each
transceiver individually before sending towards the L1.
"""
- def __init__(self, trx_list = None):
- # List of Transceiver instances
- if trx_list is not None:
- self.trx_list = trx_list
- else:
- self.trx_list = []
-
- def add_trx(self, trx):
- if trx in self.trx_list:
- log.error("TRX is already in the list")
- return
-
- self.trx_list.append(trx)
-
- def del_trx(self, trx):
- if trx not in self.trx_list:
- log.error("TRX is not in the list")
- return
-
- self.trx_list.remove(trx)
-
def forward_msg(self, src_trx, rx_msg):
- # Transform from L12TRX to TRX2L1
- tx_msg = rx_msg.gen_trx2l1()
- if tx_msg is None:
- log.error("Forwarding failed, could not transform "
- "message (%s) => dropping..." % rx_msg.desc_hdr())
+ # Originating Transceiver may use frequency hopping,
+ # so let's precalculate its Tx frequency in advance
+ tx_freq = src_trx.get_tx_freq(rx_msg.fn)
+
+ if src_trx.rf_muted:
+ del rx_msg.burst # burst bits are omited
+ rx_msg.burst = None
# Iterate over all known transceivers
for trx in self.trx_list:
@@ -79,9 +58,11 @@ class BurstForwarder:
# Check transceiver state
if not trx.running:
continue
- if trx.rx_freq != src_trx.tx_freq:
- continue
- if tx_msg.tn not in trx.ts_list:
+
+ # Match Tx/Rx frequencies of the both transceivers
+ if trx.get_rx_freq(rx_msg.fn) != tx_freq:
continue
- trx.send_data_msg(src_trx, rx_msg, tx_msg)
+ # Transform from TxMsg to RxMsg and forward
+ tx_msg = rx_msg.trans(ver = trx.data_if._hdr_ver)
+ trx.handle_data_msg(src_trx, rx_msg, tx_msg)
diff --git a/src/target/trx_toolkit/burst_gen.py b/src/target/trx_toolkit/burst_gen.py
index a7772463..22f72053 100755
--- a/src/target/trx_toolkit/burst_gen.py
+++ b/src/target/trx_toolkit/burst_gen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -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>")]
@@ -68,9 +64,9 @@ class Application(ApplicationBase):
# Init an empty DATA message
if self.argv.conn_mode == "TRX":
- msg = DATAMSG_L12TRX(ver = self.argv.hdr_ver)
+ msg = TxMsg(ver = self.argv.hdr_ver)
elif self.argv.conn_mode == "L1":
- msg = DATAMSG_TRX2L1(ver = self.argv.hdr_ver)
+ msg = RxMsg(ver = self.argv.hdr_ver)
# Generate a random frame number or use provided one
fn_init = msg.rand_fn() if self.argv.tdma_fn is None \
@@ -176,7 +172,7 @@ class Application(ApplicationBase):
help = "How many bursts to send (default %(default)s)")
bg_group.add_argument("-v", "--hdr-version", metavar = "VER",
dest = "hdr_ver", type = int,
- default = 0, choices = DATAMSG.known_versions,
+ default = 0, choices = Msg.KNOWN_VERSIONS,
help = "TRXD header version (default %(default)s)")
bg_group.add_argument("-f", "--frame-number", metavar = "FN",
dest = "tdma_fn", type = int,
diff --git a/src/target/trx_toolkit/burst_send.py b/src/target/trx_toolkit/burst_send.py
index 368031ce..27f585e0 100755
--- a/src/target/trx_toolkit/burst_send.py
+++ b/src/target/trx_toolkit/burst_send.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -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>")]
@@ -79,11 +75,10 @@ class Application(ApplicationBase):
def msg_pass_filter(self, msg):
# Direction filter
- l12trx = self.argv.conn_mode == "TRX"
- if isinstance(msg, DATAMSG_L12TRX) and not l12trx:
- return False
- elif isinstance(msg, DATAMSG_TRX2L1) and l12trx:
- return False
+ if isinstance(msg, RxMsg) and self.argv.conn_mode == "TRX":
+ return False # cannot send RxMsg to TRX
+ if isinstance(msg, TxMsg) and self.argv.conn_mode == "L1":
+ return False # cannot send TxMsg to L1
# Timeslot filter
if self.argv.pf_tn is not None:
diff --git a/src/target/trx_toolkit/clck_gen.py b/src/target/trx_toolkit/clck_gen.py
index c58d8bde..427eb88f 100755
--- a/src/target/trx_toolkit/clck_gen.py
+++ b/src/target/trx_toolkit/clck_gen.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TRX Toolkit
# Simple TDMA frame clock generator
#
-# (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
@@ -17,18 +17,14 @@
# 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>")]
+APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
import logging as log
+import threading
import signal
from app_common import ApplicationBase
-from threading import Timer
from udp_link import UDPLink
from gsm_shared import *
@@ -40,38 +36,59 @@ class CLCKGen:
# Average loop back delay
LO_DELAY_US = 90.0
- # State variables
- timer = None
-
def __init__(self, clck_links, clck_start = 0, ind_period = 102):
+ # This event is needed to control the thread
+ self._breaker = threading.Event()
+ self._thread = None
+
self.clck_links = clck_links
self.ind_period = ind_period
self.clck_start = clck_start
- self.clck_src = clck_start
# Calculate counter time
self.ctr_interval = self.GSM_FRAME_US - self.LO_DELAY_US
self.ctr_interval /= self.SEC_DELAY_US
- self.ctr_interval *= self.ind_period
+
+ # (Optional) clock consumer
+ self.clck_handler = None
+
+ @property
+ def running(self):
+ if self._thread is None:
+ return False
+ return self._thread.is_alive()
def start(self):
- # Send the first indication
- self.send_clck_ind()
+ # Make sure we won't start two threads
+ assert(self._thread is None)
+
+ # (Re)set the clock counter
+ self.clck_src = self.clck_start
+
+ # Initialize and start a new thread
+ self._thread = threading.Thread(target = self._worker)
+ self._thread.setDaemon(True)
+ self._thread.start()
def stop(self):
- # Stop pending timer
- if self.timer is not None:
- self.timer.cancel()
- self.timer = None
+ # No thread, no problem ;)
+ if self._thread is None:
+ return
- # Reset the clock source
- self.clck_src = self.clck_start
+ # Stop the thread first
+ self._breaker.set()
+ self._thread.join()
- def send_clck_ind(self):
- # Keep clock cycle
- if self.clck_src % GSM_HYPERFRAME >= 0:
- self.clck_src %= GSM_HYPERFRAME
+ # Free memory, reset breaker
+ del self._thread
+ self._thread = None
+ self._breaker.clear()
+ def _worker(self):
+ while not self._breaker.wait(self.ctr_interval):
+ self.send_clck_ind()
+
+ def send_clck_ind(self):
# We don't need to send so often
if self.clck_src % self.ind_period == 0:
# Create UDP payload
@@ -84,12 +101,11 @@ class CLCKGen:
# Debug print
log.debug(payload.rstrip("\0"))
- # Increase frame count
- self.clck_src += self.ind_period
+ if self.clck_handler is not None:
+ self.clck_handler(self.clck_src)
- # Schedule a new indication
- self.timer = Timer(self.ctr_interval, self.send_clck_ind)
- self.timer.start()
+ # Increase frame count (modular arithmetic)
+ self.clck_src = (self.clck_src + 1) % GSM_HYPERFRAME
# Just a wrapper for independent usage
class Application(ApplicationBase):
@@ -102,17 +118,19 @@ class Application(ApplicationBase):
# Configure logging
log.basicConfig(level = log.DEBUG,
- format = "[%(levelname)s] %(filename)s:%(lineno)d %(message)s")
+ format = "[%(levelname)s] TID#%(thread)s %(filename)s:%(lineno)d %(message)s")
def run(self):
self.link = UDPLink("127.0.0.1", 5800, "0.0.0.0", 5700)
self.clck = CLCKGen([self.link], ind_period = 51)
self.clck.start()
+ # Block unless we receive a signal
+ self.clck._thread.join()
+
def sig_handler(self, signum, frame):
log.info("Signal %d received" % signum)
if signum == signal.SIGINT:
- print("Stopping timer")
self.clck.stop()
if __name__ == '__main__':
diff --git a/src/target/trx_toolkit/codec.py b/src/target/trx_toolkit/codec.py
new file mode 100644
index 00000000..c5706009
--- /dev/null
+++ b/src/target/trx_toolkit/codec.py
@@ -0,0 +1,404 @@
+# -*- coding: utf-8 -*-
+
+'''
+Very simple (performance oriented) declarative message codec.
+Inspired by Pycrate and Scapy.
+'''
+
+# TRX Toolkit
+#
+# (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+# Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+#
+# All Rights Reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU 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.
+
+from typing import Optional, Callable, Tuple, Any
+import abc
+
+class ProtocolError(Exception):
+ ''' Error in a protocol definition. '''
+
+class DecodeError(Exception):
+ ''' Error during decoding of a field/message. '''
+
+class EncodeError(Exception):
+ ''' Error during encoding of a field/message. '''
+
+
+class Codec(abc.ABC):
+ ''' Base class providing encoding and decoding API. '''
+
+ @abc.abstractmethod
+ def from_bytes(self, vals: dict, data: bytes) -> int:
+ ''' Decode value(s) from the given buffer of bytes. '''
+
+ @abc.abstractmethod
+ def to_bytes(self, vals: dict) -> bytes:
+ ''' Encode value(s) into bytes. '''
+
+
+class Field(Codec):
+ ''' Base class representing one field in a Message. '''
+
+ # Default length (0 means the whole buffer)
+ DEF_LEN = 0 # type: int
+
+ # Default parameters
+ DEF_PARAMS = { } # type: dict
+
+ # Presence of a field during decoding and encoding
+ ## get_pres: Callable[[dict], bool]
+ # Length of a field for self.from_bytes()
+ ## get_len: Callable[[dict, bytes], int]
+ # Value of a field for self.to_bytes()
+ ## get_val: Callable[[dict], Any]
+
+ def __init__(self, name: str, **kw) -> None:
+ self.name = name
+
+ self.len = kw.get('len', self.DEF_LEN)
+ if self.len == 0: # flexible field
+ self.get_len = lambda _, data: len(data)
+ else: # fixed length
+ self.get_len = lambda vals, _: self.len
+
+ # Field is unconditionally present by default
+ self.get_pres = lambda vals: True
+ # Field takes its value from the given dict by default
+ self.get_val = lambda vals: vals[self.name]
+
+ # Additional parameters for derived field types
+ self.p = { key : kw.get(key, self.DEF_PARAMS[key])
+ for key in self.DEF_PARAMS }
+
+ def from_bytes(self, vals: dict, data: bytes) -> int:
+ if self.get_pres(vals) is False:
+ return 0
+ length = self.get_len(vals, data)
+ if len(data) < length:
+ raise DecodeError('Short read')
+ self._from_bytes(vals, data[:length])
+ return length
+
+ def to_bytes(self, vals: dict) -> bytes:
+ if self.get_pres(vals) is False:
+ return b''
+ data = self._to_bytes(vals)
+ if self.len > 0 and len(data) != self.len:
+ raise EncodeError('Field length mismatch')
+ return data
+
+ @abc.abstractmethod
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ ''' Decode value(s) from the given buffer of bytes. '''
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def _to_bytes(self, vals: dict) -> bytes:
+ ''' Encode value(s) into bytes. '''
+ raise NotImplementedError
+
+
+class Buf(Field):
+ ''' A sequence of octets. '''
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ vals[self.name] = data
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ # TODO: handle len(self.get_val()) < self.get_len()
+ return self.get_val(vals)
+
+
+class Spare(Field):
+ ''' Spare filling for RFU fields or padding. '''
+
+ # Default parameters
+ DEF_PARAMS = {
+ 'filler' : b'\x00',
+ }
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ pass # Just ignore it
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ return self.p['filler'] * self.get_len(vals, b'')
+
+
+class Uint(Field):
+ ''' An integer field: unsigned, N bits, big endian. '''
+
+ # Uint8 by default
+ DEF_LEN = 1
+
+ # Default parameters
+ DEF_PARAMS = {
+ 'offset' : 0,
+ 'mult' : 1,
+ }
+
+ # Big endian, unsigned
+ SIGN = False
+ BO = 'big'
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ val = int.from_bytes(data, self.BO, signed=self.SIGN)
+ vals[self.name] = val * self.p['mult'] + self.p['offset']
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ val = (self.get_val(vals) - self.p['offset']) // self.p['mult']
+ return val.to_bytes(self.len, self.BO, signed=self.SIGN)
+
+class Uint16BE(Uint):
+ DEF_LEN = 16 // 8
+
+class Uint16LE(Uint16BE):
+ BO = 'little'
+
+class Uint32BE(Uint):
+ DEF_LEN = 32 // 8
+
+class Uint32LE(Uint32BE):
+ BO = 'little'
+
+class Int(Uint):
+ SIGN = True
+
+class Int16BE(Int):
+ DEF_LEN = 16 // 8
+
+class Int16LE(Int16BE):
+ BO = 'little'
+
+class Int32BE(Int):
+ DEF_LEN = 32 // 8
+
+class Int32LE(Int32BE):
+ BO = 'little'
+
+
+class BitFieldSet(Field):
+ ''' A set of bit-fields. '''
+
+ # Default parameters
+ DEF_PARAMS = {
+ # Default field order (MSB first)
+ 'order' : 'big',
+ }
+
+ # To be defined by derived types
+ STRUCT = () # type: Tuple['BitField', ...]
+
+ def __init__(self, **kw) -> None:
+ Field.__init__(self, self.__class__.__name__, **kw)
+
+ self._fields = kw.get('set', self.STRUCT)
+ if type(self._fields) is not tuple:
+ raise ProtocolError('Expected a tuple')
+
+ # LSB first is basically reversed order
+ if self.p['order'] in ('little', 'lsb'):
+ self._fields = self._fields[::-1]
+
+ # Calculate the overall field length
+ if self.len == 0:
+ bl_sum = sum([f.bl for f in self._fields])
+ self.len = bl_sum // 8
+ if bl_sum % 8 > 0:
+ self.len += 1
+
+ # Re-define self.get_len() since we always know the length
+ self.get_len = lambda vals, data: self.len
+
+ # Pre-calculate offset and mask for each field
+ offset = self.len * 8
+ for f in self._fields:
+ if f.bl > offset:
+ raise ProtocolError(f, 'BitFieldSet overflow')
+ f.offset = offset - f.bl
+ f.mask = 2 ** f.bl - 1
+ offset -= f.bl
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ blob = int.from_bytes(data, byteorder='big') # intentionally using 'big' here
+ for f in self._fields:
+ f.dec_val(vals, blob)
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ blob = 0x00
+ for f in self._fields: # TODO: use functools.reduce()?
+ blob |= f.enc_val(vals)
+ return blob.to_bytes(self.len, byteorder='big')
+
+class BitField:
+ ''' One field in a BitFieldSet. '''
+
+ # Special fields for BitFieldSet
+ offset = 0 # type: int
+ mask = 0 # type: int
+
+ class Spare:
+ ''' Spare filling in a BitFieldSet. '''
+
+ def __init__(self, bl: int) -> None:
+ self.name = None
+ self.bl = bl
+
+ def enc_val(self, vals: dict) -> int:
+ return 0
+
+ def dec_val(self, vals: dict, blob: int) -> None:
+ pass # Just ignore it
+
+ def __init__(self, name: str, bl: int, **kw) -> None:
+ if bl < 1: # Ensure proper length
+ raise ProtocolError('Incorrect bit-field length')
+
+ self.name = name
+ self.bl = bl
+
+ # (Optional) fixed value for encoding and decoding
+ self.val = kw.get('val', None) # type: Optional[int]
+
+ def enc_val(self, vals: dict) -> int:
+ if self.val is None:
+ val = vals[self.name]
+ else:
+ val = self.val
+ return (val & self.mask) << self.offset
+
+ def dec_val(self, vals: dict, blob: int) -> None:
+ vals[self.name] = (blob >> self.offset) & self.mask
+ if (self.val is not None) and (vals[self.name] != self.val):
+ raise DecodeError('Unexpected value %d, expected %d'
+ % (vals[self.name], self.val))
+
+
+class Envelope:
+ ''' A group of related fields. '''
+
+ STRUCT = () # type: Tuple[Codec, ...]
+
+ def __init__(self, check_len: bool = True):
+ # TODO: ensure uniqueue field names in self.STRUCT
+ self.c = { } # type: dict
+ self.check_len = check_len
+
+ def __getitem__(self, key: str) -> Any:
+ return self.c[key]
+
+ def __setitem__(self, key: str, val: Any) -> None:
+ self.c[key] = val
+
+ def __delitem__(self, key: str) -> None:
+ del self.c[key]
+
+ def check(self, vals: dict) -> None:
+ ''' Check the content before encoding and after decoding.
+ Raise exceptions (e.g. ValueError) if something is wrong.
+
+ Do not assert for every possible error (e.g. a negative value
+ for a Uint field) if an exception will be thrown by the field's
+ to_bytes() method anyway. Only additional constraints here.
+ '''
+
+ def from_bytes(self, data: bytes) -> int:
+ self.c.clear() # forget the old content
+ return self._from_bytes(self.c, data)
+
+ def to_bytes(self) -> bytes:
+ return self._to_bytes(self.c)
+
+ def _from_bytes(self, vals: dict, data: bytes, offset: int = 0) -> int:
+ try: # Fields throw exceptions
+ for f in self.STRUCT:
+ offset += f.from_bytes(vals, data[offset:])
+ except Exception as e:
+ # Add contextual info
+ raise DecodeError(self, f, offset) from e
+ if self.check_len and len(data) != offset:
+ raise DecodeError(self, 'Unhandled tail octets: %s'
+ % data[offset:].hex())
+ self.check(vals) # Check the content after decoding (raises exceptions)
+ return offset
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ def proc(f: Codec):
+ try: # Fields throw exceptions
+ return f.to_bytes(vals)
+ except Exception as e:
+ # Add contextual info
+ raise EncodeError(self, f) from e
+ self.check(vals) # Check the content before encoding (raises exceptions)
+ return b''.join([proc(f) for f in self.STRUCT])
+
+ class F(Field):
+ ''' Field wrapper. '''
+
+ def __init__(self, e: 'Envelope', name: str, **kw) -> None:
+ Field.__init__(self, name, **kw)
+ self.e = e
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ vals[self.name] = { }
+ self.e._from_bytes(vals[self.name], data)
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ return self.e._to_bytes(self.get_val(vals))
+
+ def f(self, name: str, **kw) -> Field:
+ return self.F(self, name, **kw)
+
+
+class Sequence:
+ ''' A sequence of repeating elements (e.g. TLVs). '''
+
+ # The item of sequence
+ ITEM = None # type: Optional[Envelope]
+
+ def __init__(self, **kw) -> None:
+ if (self.ITEM is None) and ('item' not in kw):
+ raise ProtocolError('Missing Sequence item')
+ self._item = kw.get('item', self.ITEM) # type: Envelope
+ self._item.check_len = False
+
+ def from_bytes(self, data: bytes) -> list:
+ proc = self._item._from_bytes
+ vseq, offset = [], 0
+ length = len(data)
+
+ while offset < length:
+ vseq.append({ }) # new item of sequence
+ offset += proc(vseq[-1], data[offset:])
+
+ return vseq
+
+ def to_bytes(self, vseq: list) -> bytes:
+ proc = self._item._to_bytes
+ return b''.join([proc(v) for v in vseq])
+
+ class F(Field):
+ ''' Field wrapper. '''
+
+ def __init__(self, s: 'Sequence', name: str, **kw) -> None:
+ Field.__init__(self, name, **kw)
+ self.s = s
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ vals[self.name] = self.s.from_bytes(data)
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ return self.s.to_bytes(self.get_val(vals))
+
+ def f(self, name: str, **kw) -> Field:
+ return self.F(self, name, **kw)
diff --git a/src/target/trx_toolkit/ctrl_cmd.py b/src/target/trx_toolkit/ctrl_cmd.py
index 982cf31c..c6ac6d84 100755
--- a/src/target/trx_toolkit/ctrl_cmd.py
+++ b/src/target/trx_toolkit/ctrl_cmd.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -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 89dfe467..f9d30c19 100644
--- a/src/target/trx_toolkit/ctrl_if.py
+++ b/src/target/trx_toolkit/ctrl_if.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
# CTRL interface implementation
#
-# (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
#
# All Rights Reserved
#
@@ -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)
@@ -61,13 +61,17 @@ class CTRLInterface(UDPLink):
# Now we have something like ["TXTUNE", "941600"]
return request
- def verify_cmd(self, request, cmd, argc):
+ # If va is True, the command can have variable number of arguments
+ def verify_cmd(self, request, cmd, argc, va = False):
# Check if requested command matches
if request[0] != cmd:
return False
# And has enough arguments
- if len(request) - 1 != argc:
+ req_len = len(request[1:])
+ if not va and req_len != argc:
+ return False
+ elif va and req_len < argc:
return False
return True
@@ -82,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 e44f8a92..e6fdaf1d 100644
--- a/src/target/trx_toolkit/ctrl_if_trx.py
+++ b/src/target/trx_toolkit/ctrl_if_trx.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
# CTRL interface implementation (common commands)
#
-# (C) 2016-2019 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
#
# All Rights Reserved
#
@@ -17,15 +17,11 @@
# 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
from ctrl_if import CTRLInterface
-from data_msg import DATAMSG
+from data_msg import Msg
class CTRLInterfaceTRX(CTRLInterface):
""" CTRL interface handler for common transceiver management commands.
@@ -105,16 +101,13 @@ class CTRLInterfaceTRX(CTRLInterface):
log.error("(%s) Transceiver already started" % self.trx)
return -1
- # Ensure RX / TX freq. are set
- if (self.trx.rx_freq is None) or (self.trx.tx_freq is None):
- log.error("(%s) RX / TX freq. are not set" % self.trx)
+ # Ensure that transceiver is ready
+ if not self.trx.ready:
+ log.error("(%s) Transceiver is not ready" % self.trx)
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
@@ -134,40 +124,14 @@ class CTRLInterfaceTRX(CTRLInterface):
log.debug("(%s) Recv RXTUNE cmd" % self.trx)
# TODO: check freq range
- self.trx.rx_freq = int(request[1]) * 1000
+ self.trx._rx_freq = int(request[1]) * 1000
return 0
elif self.verify_cmd(request, "TXTUNE", 1):
log.debug("(%s) Recv TXTUNE cmd" % self.trx)
# TODO: check freq range
- 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)
-
+ self.trx._tx_freq = int(request[1]) * 1000
return 0
# Power measurement
@@ -187,6 +151,32 @@ class CTRLInterfaceTRX(CTRLInterface):
return (0, [str(meas_dbm)])
+ # Frequency hopping configuration (variable length list):
+ #
+ # CMD SETFH <HSN> <MAIO> <RXF1> <TXF1> [... <RXFN> <TXFN>]
+ #
+ # where <RXFN> and <TXFN> is a pair of Rx/Tx frequencies (in kHz)
+ # corresponding to one ARFCN the Mobile Allocation. Note that the
+ # channel list is expected to be sorted in ascending order.
+ if self.verify_cmd(request, "SETFH", 4, va = True):
+ log.debug("(%s) Recv SETFH cmd" % self.trx)
+
+ # Parse HSN and MAIO
+ hsn = int(request[1])
+ maio = int(request[2])
+
+ # Parse the list of hopping frequencies
+ ma = [int(f) * 1000 for f in request[3:]] # kHz -> Hz
+ ma = [(rx, tx) for rx, tx in zip(ma[0::2], ma[1::2])]
+
+ # Configure the hopping sequence generator
+ try:
+ self.trx.enable_fh(hsn, maio, ma)
+ return 0
+ except:
+ log.error("(%s) Failed to configure frequency hopping" % self.trx)
+ return -1
+
# TRXD header version negotiation
if self.verify_cmd(request, "SETFORMAT", 1):
log.debug("(%s) Recv SETFORMAT cmd" % self.trx)
@@ -196,7 +186,7 @@ class CTRLInterfaceTRX(CTRLInterface):
# ... and store current for logging
ver_cur = self.trx.data_if._hdr_ver
- if ver_req < 0 or ver_req > DATAMSG.CHDR_VERSION_MAX:
+ if ver_req < 0 or ver_req > Msg.CHDR_VERSION_MAX:
log.error("(%s) Incorrect TRXD header version %u"
% (self.trx, ver_req))
return -1
@@ -212,6 +202,26 @@ class CTRLInterfaceTRX(CTRLInterface):
% (self.trx, ver_cur, ver_req))
return ver_req
+ # Set Power Attenuation
+ if self.verify_cmd(request, "SETPOWER", 1):
+ log.debug("(%s) Recv SETPOWER cmd" % self.trx)
+ # Parse the requested Tx Power Attenuation
+ att_req = int(request[1])
+ self.trx.tx_att_base = att_req
+ return 0
+
+ # Retrieve Nominal Tx power
+ if self.verify_cmd(request, "NOMTXPOWER", 0):
+ log.debug("(%s) Recv NOMTXPOWER cmd" % self.trx)
+ return (0, [str(self.trx.tx_power_base)])
+
+ # Lock/Unlock RF emission+reception
+ if self.verify_cmd(request, "RFMUTE", 1):
+ log.debug("(%s) Recv RFMUTE cmd" % self.trx)
+ # Parse the requested RFMUTE state (1=locked, 0=unlocked)
+ self.trx.rf_muted = int(request[1]) > 0
+ return 0
+
# Wrong / unknown command
else:
# We don't care about other commands,
diff --git a/src/target/trx_toolkit/data_dump.py b/src/target/trx_toolkit/data_dump.py
index da42023b..8475ceb2 100644
--- a/src/target/trx_toolkit/data_dump.py
+++ b/src/target/trx_toolkit/data_dump.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -17,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
@@ -29,18 +24,18 @@ from data_msg import *
class DATADump:
# Constants
- TAG_L12TRX = b'\x01'
- TAG_TRX2L1 = b'\x02'
+ TAG_TxMsg = b'\x01'
+ TAG_RxMsg = b'\x02'
HDR_LENGTH = 3
# Generates raw bytes from a DATA message
# Return value: raw message bytes
def dump_msg(self, msg):
# Determine a message type
- if isinstance(msg, DATAMSG_L12TRX):
- tag = self.TAG_L12TRX
- elif isinstance(msg, DATAMSG_TRX2L1):
- tag = self.TAG_TRX2L1
+ if isinstance(msg, TxMsg):
+ tag = self.TAG_TxMsg
+ elif isinstance(msg, RxMsg):
+ tag = self.TAG_RxMsg
else:
raise ValueError("Unknown message type")
@@ -62,12 +57,10 @@ class DATADump:
tag = hdr[:1]
# Check if tag is known
- if tag == self.TAG_L12TRX:
- # L1 -> TRX
- msg = DATAMSG_L12TRX()
- elif tag == self.TAG_TRX2L1:
- # TRX -> L1
- msg = DATAMSG_TRX2L1()
+ if tag == self.TAG_TxMsg:
+ msg = TxMsg()
+ elif tag == self.TAG_RxMsg:
+ msg = RxMsg()
else:
# Unknown tag
return False
@@ -84,8 +77,6 @@ class DATADumpFile(DATADump):
self.f = capture
def __del__(self):
- # FIXME: this causes an Exception in Python 2 (but not in Python 3)
- # AttributeError: 'NoneType' object has no attribute 'info'
log.info("Closing the capture file")
self.f.close()
@@ -94,11 +85,11 @@ class DATADumpFile(DATADump):
# True in case of success,
# or False in case of EOF or header parsing error.
def _seek2msg(self, idx):
- # Seek to the begining of the capture
+ # Seek to the beginning of the capture
self.f.seek(0)
# Read the capture in loop...
- for i in range(idx):
+ for _ in range(idx):
# Attempt to read a message header
hdr_raw = self.f.read(self.HDR_LENGTH)
if len(hdr_raw) != self.HDR_LENGTH:
@@ -158,14 +149,14 @@ class DATADumpFile(DATADump):
# Parses a particular message defined by index idx
# Return value:
# a parsed message in case of success,
- # or None in case of EOF or header parsing error,
- # or False in case of message parsing error or out of range.
+ # or None in case of EOF, out of range, or header parsing error,
+ # or False in case of message parsing error.
def parse_msg(self, idx):
- # Move descriptor to the begining of requested message
+ # Move descriptor to the beginning of requested message
rc = self._seek2msg(idx)
if not rc:
log.error("Couldn't find requested message")
- return False
+ return None
# Attempt to parse a message
return self._parse_msg()
@@ -179,7 +170,7 @@ class DATADumpFile(DATADump):
# Should we skip some messages?
if skip is None:
- # Seek to the begining of the capture
+ # Seek to the beginning of the capture
self.f.seek(0)
else:
rc = self._seek2msg(skip)
@@ -220,148 +211,3 @@ class DATADumpFile(DATADump):
def append_all(self, msgs):
for msg in msgs:
self.append_msg(msg)
-
-# Regression tests
-if __name__ == '__main__':
- from tempfile import TemporaryFile
- from gsm_shared import *
- import random
-
- # Configure logging
- log.basicConfig(level = log.DEBUG,
- format = "[%(levelname)s] %(filename)s:%(lineno)d %(message)s")
-
- # Create a temporary file
- tf = TemporaryFile()
-
- # Create an instance of DATA dump manager
- ddf = DATADumpFile(tf)
-
- # Generate two random bursts
- burst_l12trx = []
- burst_trx2l1 = []
-
- for i in range(0, GSM_BURST_LEN):
- ubit = random.randint(0, 1)
- burst_l12trx.append(ubit)
-
- sbit = random.randint(-127, 127)
- burst_trx2l1.append(sbit)
-
- # Generate a basic list of random messages
- log.info("Generating the reference messages")
- messages_ref = []
-
- for i in range(100):
- # Create a message
- if i % 2:
- msg = DATAMSG_L12TRX()
- msg.burst = burst_l12trx
- else:
- msg = DATAMSG_TRX2L1()
- msg.burst = burst_trx2l1
-
- # Randomize the header
- msg.rand_hdr()
-
- # Append
- messages_ref.append(msg)
-
- log.info("Adding the following messages to the capture:")
- for msg in messages_ref[:3]:
- log.info("%s: burst_len=%d"
- % (msg.desc_hdr(), len(msg.burst)))
-
- # Check single message appending
- ddf.append_msg(messages_ref[0])
- ddf.append_msg(messages_ref[1])
- ddf.append_msg(messages_ref[2])
-
- # Read the written messages back
- messages_check = ddf.parse_all()
-
- log.info("Read the following messages back:")
- for msg in messages_check:
- log.info("%s: burst_len=%d"
- % (msg.desc_hdr(), len(msg.burst)))
-
- # Expecting three messages
- assert(len(messages_check) == 3)
-
- # Check the messages
- for i in range(3):
- # Compare common header parts and bursts
- assert(messages_check[i].burst == messages_ref[i].burst)
- assert(messages_check[i].fn == messages_ref[i].fn)
- assert(messages_check[i].tn == messages_ref[i].tn)
-
- # Validate a message
- messages_check[i].validate()
-
- log.info("Check append_msg(): OK")
-
-
- # Append the pending reference messages
- ddf.append_all(messages_ref[3:])
-
- # Read the written messages back
- messages_check = ddf.parse_all()
-
- # Check the final amount
- assert(len(messages_check) == len(messages_ref))
-
- # Check the messages
- for i in range(len(messages_check)):
- # Compare common header parts and bursts
- assert(messages_check[i].burst == messages_ref[i].burst)
- assert(messages_check[i].fn == messages_ref[i].fn)
- assert(messages_check[i].tn == messages_ref[i].tn)
-
- # Validate a message
- messages_check[i].validate()
-
- log.info("Check append_all(): OK")
-
-
- # Check parse_msg()
- msg0 = ddf.parse_msg(0)
- msg10 = ddf.parse_msg(10)
-
- # Make sure parsing was successful
- assert(msg0 and msg10)
-
- # Compare common header parts and bursts
- assert(msg0.burst == messages_ref[0].burst)
- assert(msg0.fn == messages_ref[0].fn)
- assert(msg0.tn == messages_ref[0].tn)
-
- assert(msg10.burst == messages_ref[10].burst)
- assert(msg10.fn == messages_ref[10].fn)
- assert(msg10.tn == messages_ref[10].tn)
-
- # Validate both messages
- msg0.validate()
- msg10.validate()
-
- log.info("Check parse_msg(): OK")
-
-
- # Check parse_all() with range
- messages_check = ddf.parse_all(skip = 10, count = 20)
-
- # Make sure parsing was successful
- assert(messages_check)
-
- # Check the amount
- assert(len(messages_check) == 20)
-
- for i in range(20):
- # Compare common header parts and bursts
- assert(messages_check[i].burst == messages_ref[i + 10].burst)
- assert(messages_check[i].fn == messages_ref[i + 10].fn)
- assert(messages_check[i].tn == messages_ref[i + 10].tn)
-
- # Validate a message
- messages_check[i].validate()
-
- log.info("Check parse_all(): OK")
diff --git a/src/target/trx_toolkit/data_if.py b/src/target/trx_toolkit/data_if.py
index 418c3f8a..8e808943 100644
--- a/src/target/trx_toolkit/data_if.py
+++ b/src/target/trx_toolkit/data_if.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -17,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
@@ -36,7 +31,7 @@ class DATAInterface(UDPLink):
log.debug("Init TRXD interface (%s)" % self.desc_link())
def set_hdr_ver(self, ver):
- if not ver in DATAMSG.known_versions:
+ if not ver in Msg.KNOWN_VERSIONS:
return False
self._hdr_ver = ver
@@ -44,7 +39,7 @@ class DATAInterface(UDPLink):
def pick_hdr_ver(self, ver_req):
# Pick a version that is lower or equal to ver_req
- for ver in DATAMSG.known_versions[::-1]:
+ for ver in Msg.KNOWN_VERSIONS[::-1]:
if ver <= ver_req:
return ver
@@ -65,16 +60,16 @@ class DATAInterface(UDPLink):
data, _ = self.sock.recvfrom(512)
return data
- def recv_l12trx_msg(self):
+ def recv_tx_msg(self):
# Read raw data from socket
data = self.recv_raw_data()
- # Attempt to parse as a L12TRX message
+ # Attempt to parse a TRXD Tx message
try:
- msg = DATAMSG_L12TRX()
+ msg = TxMsg()
msg.parse_msg(bytearray(data))
except:
- log.error("Failed to parse a L12TRX message "
+ log.error("Failed to parse a TRXD Tx message "
"from R:%s:%u" % (self.remote_addr, self.remote_port))
return None
@@ -85,16 +80,16 @@ class DATAInterface(UDPLink):
return msg
- def recv_trx2l1_msg(self):
+ def recv_rx_msg(self):
# Read raw data from socket
data = self.recv_raw_data()
- # Attempt to parse as a L12TRX message
+ # Attempt to parse a TRXD Rx message
try:
- msg = DATAMSG_TRX2L1()
+ msg = RxMsg()
msg.parse_msg(bytearray(data))
except:
- log.error("Failed to parse a TRX2L1 message "
+ log.error("Failed to parse a TRXD Rx message "
"from R:%s:%u" % (self.remote_addr, self.remote_port))
return None
@@ -106,11 +101,14 @@ class DATAInterface(UDPLink):
return msg
def send_msg(self, msg, legacy = False):
- # Validate a message
- msg.validate()
-
- # Generate TRX message
- payload = msg.gen_msg(legacy)
+ try:
+ # Validate and encode a TRXD message
+ payload = msg.gen_msg(legacy)
+ except ValueError as e:
+ log.error("Failed to encode a TRXD message ('%s') "
+ "due to error: %s" % (msg.desc_hdr(), e))
+ # TODO: we may want to send a NOPE.ind here
+ return
# Send message
self.send(payload)
diff --git a/src/target/trx_toolkit/data_msg.py b/src/target/trx_toolkit/data_msg.py
index ec59b856..898a4ae6 100644
--- a/src/target/trx_toolkit/data_msg.py
+++ b/src/target/trx_toolkit/data_msg.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -17,24 +16,24 @@
# 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
+import abc
+from typing import List
from enum import Enum
from gsm_shared import *
class Modulation(Enum):
""" Modulation types defined in 3GPP TS 45.002 """
- ModGMSK = (0b0000, 148)
- Mod8PSK = (0b0100, 444)
- ModAQPSK = (0b0110, 296)
- Mod16QAM = (0b1000, 592)
- Mod32QAM = (0b1010, 740)
+ ModGMSK = (0b0000, 1 * GMSK_BURST_LEN)
+ Mod8PSK = (0b0100, 3 * GMSK_BURST_LEN)
+ ModGMSK_AB = (0b0110, 1 * GMSK_BURST_LEN)
+ # ModRFU = (0b0111, 0) # Reserved for Future Use
+ Mod16QAM = (0b1000, 4 * GMSK_BURST_LEN)
+ Mod32QAM = (0b1010, 5 * GMSK_BURST_LEN)
+ ModAQPSK = (0b1100, 2 * GMSK_BURST_LEN)
def __init__(self, coding, bl):
# Coding in TRXD header
@@ -56,121 +55,60 @@ class Modulation(Enum):
return mod
return None
-class DATAMSG:
- """ TRXD (DATA) message codec (common part).
-
- The DATA messages are used to carry bursts in both directions
- between L1 and TRX. There exist two kinds of them:
-
- - L12TRX (L1 -> TRX) - to be transmitted bursts,
- - TRX2L1 (TRX -> L1) - received bursts.
-
- Both of them have quite similar structure, and start with
- the common fixed-size message header (no TLVs):
-
- +---------------+-----------------+------------+
- | common header | specific header | burst bits |
- +---------------+-----------------+------------+
-
- while the message specific headers and bit types are different.
-
- The common header is represented by this class, which is the
- parent of both DATAMSG_L12TRX and DATAMSG_TRX2L2 (see below),
- and has the following fields:
-
- +-----------------+----------------+-------------------+
- | VER (1/2 octet) | TN (1/2 octet) | FN (4 octets, BE) |
- +-----------------+----------------+-------------------+
-
- where:
-
- - VER is the header version indicator (1/2 octet MSB),
- - TN is TDMA time-slot number (1/2 octet LSB), and
- - FN is TDMA frame number (4 octets, big endian).
-
- == Header version indication
-
- It may be necessary to extend the message specific header
- with more information. Since this is not a TLV-based
- protocol, we need to include the header format version.
-
- +-----------------+------------------------+
- | 7 6 5 4 3 2 1 0 | bit numbers |
- +-----------------+------------------------+
- | X X X X . . . . | header version (0..15) |
- +-----------------+------------------------+
- | . . . . . X X X | TDMA TN (0..7) |
- +-----------------+------------------------+
- | . . . . X . . . | RESERVED (0) |
- +-----------------+------------------------+
-
- Instead of prepending an additional byte, it was decided to use
- 4 MSB bits of the first octet, which used to be zero-initialized
- due to the value range of TDMA TN. Therefore, the legacy header
- format has implicit version 0x00.
-
- Otherwise Wireshark (or trx_sniff.py) would need to guess the
- header version, or alternatively follow the control channel
- looking for the version setting command.
-
- The reserved bit number 3 can be used in the future to extend
- the TDMA TN range to (0..15), in case anybody would need
- to transfer UMTS bursts.
-
- """
+class Msg(abc.ABC):
+ ''' TRXD (DATA) message coding API (common part). '''
# NOTE: up to 16 versions can be encoded
CHDR_VERSION_MAX = 0b1111
- known_versions = [0x00, 0x01]
+ KNOWN_VERSIONS = (0, 1)
- # Common constructor
def __init__(self, fn = None, tn = None, burst = None, ver = 0):
self.burst = burst
self.ver = ver
self.fn = fn
self.tn = tn
- # The common header length
@property
def CHDR_LEN(self):
- # (VER + TN) + FN
- return 1 + 4
+ ''' The common header length. '''
+ return 1 + 4 # (VER + TN) + FN
- # Generates message specific header
+ @abc.abstractmethod
def gen_hdr(self):
- raise NotImplementedError
+ ''' Generate message specific header. '''
- # Parses message specific header
+ @abc.abstractmethod
def parse_hdr(self, hdr):
- raise NotImplementedError
+ ''' Parse message specific header. '''
- # Generates message specific burst
+ @abc.abstractmethod
def gen_burst(self):
- raise NotImplementedError
+ ''' Generate message specific burst. '''
- # Parses message specific burst
+ @abc.abstractmethod
def parse_burst(self, burst):
- raise NotImplementedError
+ ''' Parse message specific burst. '''
- # Generate a random message specific burst
+ @abc.abstractmethod
def rand_burst(self):
- raise NotImplementedError
+ ''' Generate a random message specific burst. '''
- # Generates a random frame number
def rand_fn(self):
+ ''' Generate a random frame number. '''
return random.randint(0, GSM_HYPERFRAME)
- # Generates a random timeslot number
def rand_tn(self):
+ ''' Generate a random timeslot number. '''
return random.randint(0, 7)
- # Randomizes the message header
def rand_hdr(self):
+ ''' Randomize the message header. '''
self.fn = self.rand_fn()
self.tn = self.rand_tn()
- # Generates human-readable header description
def desc_hdr(self):
+ ''' Generate human-readable header description. '''
+
result = ""
if self.ver > 0:
@@ -187,52 +125,30 @@ class DATAMSG:
return result
- # Converts unsigned soft-bits {254..0} to soft-bits {-127..127}
@staticmethod
- def usbit2sbit(bits):
- buf = []
-
- for bit in bits:
- if bit == 0xff:
- buf.append(-127)
- else:
- buf.append(127 - bit)
+ def usbit2sbit(bits: List[int]) -> List[int]:
+ ''' Convert unsigned soft-bits {254..0} to soft-bits {-127..127}. '''
+ return [-127 if (b == 0xff) else 127 - b for b in bits]
- return buf
-
- # Converts soft-bits {-127..127} to unsigned soft-bits {254..0}
@staticmethod
- def sbit2usbit(bits):
- buf = []
-
- for bit in bits:
- buf.append(127 - bit)
-
- return buf
+ def sbit2usbit(bits: List[int]) -> List[int]:
+ ''' Convert soft-bits {-127..127} to unsigned soft-bits {254..0}. '''
+ return [127 - b for b in bits]
- # Converts soft-bits {-127..127} to bits {1..0}
@staticmethod
- def sbit2ubit(bits):
- buf = []
-
- for bit in bits:
- buf.append(1 if bit < 0 else 0)
-
- return buf
+ def sbit2ubit(bits: List[int]) -> List[int]:
+ ''' Convert soft-bits {-127..127} to bits {1..0}. '''
+ return [int(b < 0) for b in bits]
- # Converts bits {1..0} to soft-bits {-127..127}
@staticmethod
- def ubit2sbit(bits):
- buf = []
+ def ubit2sbit(bits: List[int]) -> List[int]:
+ ''' Convert bits {1..0} to soft-bits {-127..127}. '''
+ return [-127 if b else 127 for b in bits]
- for bit in bits:
- buf.append(-127 if bit else 127)
-
- return buf
-
- # Validates the message fields (throws ValueError)
def validate(self):
- if not self.ver in self.known_versions:
+ ''' Validate the message fields (throws ValueError). '''
+
+ if not self.ver in self.KNOWN_VERSIONS:
raise ValueError("Unknown TRXD header version %d" % self.ver)
if self.fn is None:
@@ -247,8 +163,9 @@ class DATAMSG:
if self.tn < 0 or self.tn > 7:
raise ValueError("TDMA time-slot %d is out of range" % self.tn)
- # Generates a TRX DATA message
def gen_msg(self, legacy = False):
+ ''' Generate a TRX DATA message. '''
+
# Validate all the fields
self.validate()
@@ -276,15 +193,16 @@ class DATAMSG:
return buf
- # Parses a TRX DATA message
def parse_msg(self, msg):
+ ''' Parse a TRX DATA message. '''
+
# Make sure we have at least common header
if len(msg) < self.CHDR_LEN:
raise ValueError("Message is to short: missing common header")
# Parse the header version first
self.ver = (msg[0] >> 4)
- if not self.ver in self.known_versions:
+ if not self.ver in self.KNOWN_VERSIONS:
raise ValueError("Unknown TRXD header version %d" % self.ver)
# Parse TDMA TN and FN
@@ -306,27 +224,8 @@ class DATAMSG:
else:
self.burst = None
-class DATAMSG_L12TRX(DATAMSG):
- """ L12TRX (L1 -> TRX) message codec.
-
- This message represents a Downlink burst on the BTS side,
- or an Uplink burst on the MS side, and has the following
- message specific fixed-size header preceding the burst bits:
-
- == Versions 0x00, 0x01
-
- +-----+--------------------+
- | PWR | hard-bits (1 or 0) |
- +-----+--------------------+
-
- where PWR (1 octet) is relative (to the full-scale amplitude)
- transmit power level in dB. The absolute value is set on
- the control interface.
-
- Each hard-bit (1 or 0) of the burst is represented using one
- byte (0x01 or 0x00 respectively).
-
- """
+class TxMsg(Msg):
+ ''' Tx (L1 -> TRX) message coding API. '''
# Constants
PWR_MIN = 0x00
@@ -335,9 +234,10 @@ class DATAMSG_L12TRX(DATAMSG):
# Specific message fields
pwr = None
- # Calculates header length depending on its version
@property
def HDR_LEN(self):
+ ''' Calculate header length depending on its version. '''
+
# Common header length
length = self.CHDR_LEN
@@ -349,10 +249,11 @@ class DATAMSG_L12TRX(DATAMSG):
return length
- # Validates the message fields (throws ValueError)
def validate(self):
+ ''' Validate the message fields (throws ValueError). '''
+
# Validate common fields
- DATAMSG.validate(self)
+ Msg.validate(self)
if self.pwr is None:
raise ValueError("Tx Attenuation level is not set")
@@ -365,11 +266,12 @@ class DATAMSG_L12TRX(DATAMSG):
raise ValueError("Tx burst bits are not set")
# FIXME: properly handle IDLE / NOPE indications
- if len(self.burst) not in (GSM_BURST_LEN, EDGE_BURST_LEN):
+ if len(self.burst) not in (GMSK_BURST_LEN, EDGE_BURST_LEN):
raise ValueError("Tx burst has odd length %u" % len(self.burst))
- # Generates a random power level
def rand_pwr(self, min = None, max = None):
+ ''' Generate a random power level. '''
+
if min is None:
min = self.PWR_MIN
@@ -378,15 +280,17 @@ class DATAMSG_L12TRX(DATAMSG):
return random.randint(min, max)
- # Randomizes message specific header
def rand_hdr(self):
- DATAMSG.rand_hdr(self)
+ ''' Randomize message specific header. '''
+
+ Msg.rand_hdr(self)
self.pwr = self.rand_pwr()
- # Generates human-readable header description
def desc_hdr(self):
+ ''' Generate human-readable header description. '''
+
# Describe the common part
- result = DATAMSG.desc_hdr(self)
+ result = Msg.desc_hdr(self)
if self.pwr is not None:
result += ("pwr=%u " % self.pwr)
@@ -394,8 +298,9 @@ class DATAMSG_L12TRX(DATAMSG):
# Strip useless whitespace and return
return result.strip()
- # Generates message specific header part
def gen_hdr(self):
+ ''' Generate message specific header part. '''
+
# Allocate an empty byte-array
buf = bytearray()
@@ -404,136 +309,50 @@ class DATAMSG_L12TRX(DATAMSG):
return buf
- # Parses message specific header part
def parse_hdr(self, hdr):
+ ''' Parse message specific header part. '''
+
# Parse power level
self.pwr = hdr[5]
- # Generates message specific burst
def gen_burst(self):
+ ''' Generate message specific burst. '''
+
# Copy burst 'as is'
return bytearray(self.burst)
- # Parses message specific burst
def parse_burst(self, burst):
+ ''' Parse message specific burst. '''
+
length = len(burst)
# Distinguish between GSM and EDGE
if length >= EDGE_BURST_LEN:
self.burst = list(burst[:EDGE_BURST_LEN])
else:
- self.burst = list(burst[:GSM_BURST_LEN])
+ self.burst = list(burst[:GMSK_BURST_LEN])
- # Generate a random message specific burst
- def rand_burst(self, length = GSM_BURST_LEN):
- self.burst = []
+ def rand_burst(self, length = GMSK_BURST_LEN):
+ ''' Generate a random message specific burst. '''
+ self.burst = [random.randint(0, 1) for _ in range(length)]
- for i in range(length):
- ubit = random.randint(0, 1)
- self.burst.append(ubit)
+ def trans(self, ver = None):
+ ''' Transform this message into RxMsg. '''
- # Transforms this message to TRX2L1 message
- def gen_trx2l1(self, ver = None):
# Allocate a new message
- msg = DATAMSG_TRX2L1(fn = self.fn, tn = self.tn,
+ msg = RxMsg(fn = self.fn, tn = self.tn,
ver = self.ver if ver is None else ver)
# Convert burst bits
if self.burst is not None:
msg.burst = self.ubit2sbit(self.burst)
+ else:
+ msg.nope_ind = True
return msg
-class DATAMSG_TRX2L1(DATAMSG):
- """ TRX2L1 (TRX -> L1) message codec.
-
- This message represents an Uplink burst on the BTS side,
- or a Downlink burst on the MS side, and has the following
- message specific fixed-size header preceding the burst bits:
-
- == Version 0x00
-
- +------+-----+--------------------+
- | RSSI | ToA | soft-bits (254..0) |
- +------+-----+--------------------+
-
- == Version 0x01
-
- +------+-----+-----+-----+--------------------+
- | RSSI | ToA | MTS | C/I | soft-bits (254..0) |
- +------+-----+-----+-----+--------------------+
-
- where:
-
- - RSSI (1 octet) - Received Signal Strength Indication
- encoded without the negative sign.
- - ToA (2 octets) - Timing of Arrival in units of 1/256
- of symbol (big endian).
- - MTS (1 octet) - Modulation and Training Sequence info.
- - C/I (2 octets) - Carrier-to-Interference ratio (big endian).
-
- == Coding of MTS: Modulation and Training Sequence info
-
- 3GPP TS 45.002 version 15.1.0 defines several modulation types,
- and a few sets of training sequences for each type. The most
- common are GMSK and 8-PSK (which is used in EDGE).
-
- +-----------------+---------------------------------------+
- | 7 6 5 4 3 2 1 0 | bit numbers (value range) |
- +-----------------+---------------------------------------+
- | X . . . . . . . | IDLE / nope frame indication (0 or 1) |
- +-----------------+---------------------------------------+
- | . X X X X . . . | Modulation, TS set number (see below) |
- +-----------------+---------------------------------------+
- | . . . . . X X X | Training Sequence Code (0..7) |
- +-----------------+---------------------------------------+
-
- The bit number 7 (MSB) is set to high when either nothing has been
- detected, or during IDLE frames, so we can deliver noise levels,
- and avoid clock gaps on the L1 side. Other bits are ignored,
- and should be set to low (0) in this case.
-
- == Coding of modulation and TS set number
-
- GMSK has 4 sets of training sequences (see tables 5.2.3a-d),
- while 8-PSK (see tables 5.2.3f-g) and the others have 2 sets.
- Access and Synchronization bursts also have several synch.
- sequences.
-
- +-----------------+---------------------------------------+
- | 7 6 5 4 3 2 1 0 | bit numbers (value range) |
- +-----------------+---------------------------------------+
- | . 0 0 X X . . . | GMSK, 4 TS sets (0..3) |
- +-----------------+---------------------------------------+
- | . 0 1 0 X . . . | 8-PSK, 2 TS sets (0..1) |
- +-----------------+---------------------------------------+
- | . 0 1 1 X . . . | AQPSK, 2 TS sets (0..1) |
- +-----------------+---------------------------------------+
- | . 1 0 0 X . . . | 16QAM, 2 TS sets (0..1) |
- +-----------------+---------------------------------------+
- | . 1 0 1 X . . . | 32QAM, 2 TS sets (0..1) |
- +-----------------+---------------------------------------+
- | . 1 1 1 X . . . | RESERVED (0) |
- +-----------------+---------------------------------------+
-
- == C/I: Carrier-to-Interference ratio
-
- The C/I value can be computed from the training sequence of each
- burst, where we can compare the "ideal" training sequence with
- the actual training sequence and then express that in centiBels.
-
- == Coding of the burst bits
-
- Unlike the transmitted bursts, the received bursts are designated
- using the soft-bits notation, so the receiver can indicate its
- assurance from 0 to -127 that a given bit is 1, and from 0 to +127
- that a given bit is 0. The Viterbi algorithm allows to approximate
- the original sequence of hard-bits (1 or 0) using these values.
-
- Each soft-bit (-127..127) of the burst is encoded as an unsigned
- value in range (0..255) respectively using the constant shift.
-
- """
+class RxMsg(Msg):
+ ''' Rx (TRX -> L1) message coding API. '''
# rxlev2dbm(0..63) gives us [-110..-47], plus -10 dbm for noise
RSSI_MIN = -120
@@ -565,9 +384,10 @@ class DATAMSG_TRX2L1(DATAMSG):
tsc = None
ci = None
- # Calculates header length depending on its version
@property
def HDR_LEN(self):
+ ''' Calculate header length depending on its version. '''
+
# Common header length
length = self.CHDR_LEN
@@ -589,7 +409,7 @@ class DATAMSG_TRX2L1(DATAMSG):
raise ValueError("Rx burst bits are not set")
# ... and can be either of GSM (GMSK) or EDGE (8-PSK)
- if len(self.burst) not in (GSM_BURST_LEN, EDGE_BURST_LEN):
+ if len(self.burst) not in (GMSK_BURST_LEN, EDGE_BURST_LEN):
raise ValueError("Rx burst has odd length %u" % len(self.burst))
def _validate_burst_v1(self):
@@ -606,17 +426,19 @@ class DATAMSG_TRX2L1(DATAMSG):
if len(self.burst) != self.mod_type.bl:
raise ValueError("Rx burst has odd length %u" % len(self.burst))
- # Validates the burst (throws ValueError)
def validate_burst(self):
+ ''' Validate the burst (throws ValueError). '''
+
if self.ver == 0x00:
self._validate_burst_v0()
elif self.ver >= 0x01:
self._validate_burst_v1()
- # Validates the message header fields (throws ValueError)
def validate(self):
+ ''' Validate the message header fields (throws ValueError). '''
+
# Validate common fields
- DATAMSG.validate(self)
+ Msg.validate(self)
if self.rssi is None:
raise ValueError("RSSI is not set")
@@ -630,7 +452,8 @@ class DATAMSG_TRX2L1(DATAMSG):
if self.toa256 < self.TOA256_MIN or self.toa256 > self.TOA256_MAX:
raise ValueError("ToA256 %d is out of range" % self.toa256)
- if self.ver >= 0x01:
+ # Version specific parameters (omited for NOPE.ind)
+ if self.ver >= 0x01 and not self.nope_ind:
if type(self.mod_type) is not Modulation:
raise ValueError("Unknown Rx modulation type")
@@ -650,6 +473,8 @@ class DATAMSG_TRX2L1(DATAMSG):
if self.tsc not in self.TSC_RANGE:
raise ValueError("TSC %d is out of range" % self.tsc)
+ # Version specific parameters (also present in NOPE.ind)
+ if self.ver >= 0x01:
if self.ci is None:
raise ValueError("C/I is not set")
@@ -658,8 +483,9 @@ class DATAMSG_TRX2L1(DATAMSG):
self.validate_burst()
- # Generates a random RSSI value
def rand_rssi(self, min = None, max = None):
+ ''' Generate a random RSSI value. '''
+
if min is None:
min = self.RSSI_MIN
@@ -668,8 +494,9 @@ class DATAMSG_TRX2L1(DATAMSG):
return random.randint(min, max)
- # Generates a ToA (Time of Arrival) value
def rand_toa256(self, min = None, max = None):
+ ''' Generate a random ToA (Time of Arrival) value. '''
+
if min is None:
min = self.TOA256_MIN
@@ -678,9 +505,10 @@ class DATAMSG_TRX2L1(DATAMSG):
return random.randint(min, max)
- # Randomizes message specific header
def rand_hdr(self):
- DATAMSG.rand_hdr(self)
+ ''' Randomize message specific header. '''
+
+ Msg.rand_hdr(self)
self.rssi = self.rand_rssi()
self.toa256 = self.rand_toa256()
@@ -695,10 +523,11 @@ class DATAMSG_TRX2L1(DATAMSG):
# C/I: Carrier-to-Interference ratio
self.ci = random.randint(self.CI_MIN, self.CI_MAX)
- # Generates human-readable header description
def desc_hdr(self):
+ ''' Generate human-readable header description. '''
+
# Describe the common part
- result = DATAMSG.desc_hdr(self)
+ result = Msg.desc_hdr(self)
if self.rssi is not None:
result += ("rssi=%d " % self.rssi)
@@ -722,8 +551,9 @@ class DATAMSG_TRX2L1(DATAMSG):
# Strip useless whitespace and return
return result.strip()
- # Encodes Modulation and Training Sequence info
def gen_mts(self):
+ ''' Encode Modulation and Training Sequence info. '''
+
# IDLE / nope indication has no MTS info
if self.nope_ind:
return self.NOPE_IND
@@ -737,8 +567,9 @@ class DATAMSG_TRX2L1(DATAMSG):
return mts
- # Parses Modulation and Training Sequence info
def parse_mts(self, mts):
+ ''' Parse Modulation and Training Sequence info. '''
+
# IDLE / nope indication has no MTS info
self.nope_ind = (mts & self.NOPE_IND) > 0
if self.nope_ind:
@@ -761,8 +592,9 @@ class DATAMSG_TRX2L1(DATAMSG):
self.mod_type = Modulation.ModGMSK
self.tsc_set = mts & 0b11
- # Generates message specific header part
def gen_hdr(self):
+ ''' Generate message specific header part. '''
+
# Allocate an empty byte-array
buf = bytearray()
@@ -779,15 +611,13 @@ class DATAMSG_TRX2L1(DATAMSG):
buf.append(mts)
# C/I: Carrier-to-Interference ratio (in centiBels)
- if not self.nope_ind:
- buf += struct.pack(">h", self.ci)
- else:
- buf += bytearray(2)
+ buf += struct.pack(">h", self.ci)
return buf
- # Parses message specific header part
def parse_hdr(self, hdr):
+ ''' Parse message specific header part. '''
+
# Parse RSSI
self.rssi = -(hdr[5])
@@ -799,21 +629,20 @@ class DATAMSG_TRX2L1(DATAMSG):
self.parse_mts(hdr[8])
# C/I: Carrier-to-Interference ratio (in centiBels)
- if not self.nope_ind:
- self.ci = struct.unpack(">h", hdr[9:11])[0]
- else:
- self.ci = None
+ self.ci = struct.unpack(">h", hdr[9:11])[0]
- # Generates message specific burst
def gen_burst(self):
+ ''' Generate message specific burst. '''
+
# Convert soft-bits to unsigned soft-bits
burst_usbits = self.sbit2usbit(self.burst)
# Encode to bytes
return bytearray(burst_usbits)
- # Parses message specific burst for header version 0
def _parse_burst_v0(self, burst):
+ ''' Parse message specific burst for header version 0. '''
+
bl = len(burst)
# We need to guess modulation by the length of burst
@@ -827,8 +656,9 @@ class DATAMSG_TRX2L1(DATAMSG):
return burst[:self.mod_type.bl]
- # Parses message specific burst
def parse_burst(self, burst):
+ ''' Parse message specific burst. '''
+
burst = list(burst)
if self.ver == 0x00:
@@ -837,21 +667,19 @@ class DATAMSG_TRX2L1(DATAMSG):
# Convert unsigned soft-bits to soft-bits
self.burst = self.usbit2sbit(burst)
- # Generate a random message specific burst
def rand_burst(self, length = None):
- self.burst = []
+ ''' Generate a random message specific burst. '''
if length is None:
length = self.mod_type.bl
- for i in range(length):
- sbit = random.randint(-127, 127)
- self.burst.append(sbit)
+ self.burst = [random.randint(-127, 127) for _ in range(length)]
+
+ def trans(self, ver = None):
+ ''' Transform this message to TxMsg. '''
- # Transforms this message to L12TRX message
- def gen_l12trx(self, ver = None):
# Allocate a new message
- msg = DATAMSG_L12TRX(fn = self.fn, tn = self.tn,
+ msg = TxMsg(fn = self.fn, tn = self.tn,
ver = self.ver if ver is None else ver)
# Convert burst bits
@@ -859,215 +687,3 @@ class DATAMSG_TRX2L1(DATAMSG):
msg.burst = self.sbit2ubit(self.burst)
return msg
-
-# Regression test
-if __name__ == '__main__':
- import logging as log
-
- # Configure logging
- log.basicConfig(level = log.DEBUG,
- format = "[%(levelname)s] %(filename)s:%(lineno)d %(message)s")
-
- log.info("Generating the reference messages")
-
- # Create messages of both types
- msg_l12trx_ref = DATAMSG_L12TRX()
- msg_trx2l1_ref = DATAMSG_TRX2L1()
-
- # Validate header randomization
- for i in range(0, 100):
- msg_l12trx_ref.rand_hdr()
- msg_trx2l1_ref.rand_hdr()
-
- msg_l12trx_ref.rand_burst()
- msg_trx2l1_ref.rand_burst()
-
- msg_l12trx_ref.validate()
- msg_trx2l1_ref.validate()
-
- log.info("Validate header randomization: OK")
-
- # Test error handling for common fields
- msg = DATAMSG()
-
- # Make sure that message validation throws a ValueError
- def validate_throw(msg):
- try:
- msg.validate()
- return False
- except ValueError:
- return True
-
- # Unknown version
- msg.rand_hdr()
- msg.ver = 100
- assert(validate_throw(msg))
-
- # Uninitialized field
- msg.rand_hdr()
- msg.fn = None
- assert(validate_throw(msg))
-
- # Out-of-range value
- msg.rand_hdr()
- msg.tn = 10
- assert(validate_throw(msg))
-
- log.info("Check incorrect message validation: OK")
-
- log.info("Encoding the reference messages")
-
- # Encode DATA messages
- l12trx_raw = msg_l12trx_ref.gen_msg()
- trx2l1_raw = msg_trx2l1_ref.gen_msg()
-
- # Encode a TRX2L1 message in legacy mode
- trx2l1_raw_legacy = msg_trx2l1_ref.gen_msg(legacy = True)
-
- log.info("Parsing generated messages back")
-
- # Parse generated DATA messages
- msg_l12trx_dec = DATAMSG_L12TRX()
- msg_trx2l1_dec = DATAMSG_TRX2L1()
- msg_l12trx_dec.parse_msg(l12trx_raw)
- msg_trx2l1_dec.parse_msg(trx2l1_raw)
-
- # Parse generated TRX2L1 message in legacy mode
- msg_trx2l1_legacy_dec = DATAMSG_TRX2L1()
- msg_trx2l1_legacy_dec.parse_msg(trx2l1_raw_legacy)
-
- log.info("Comparing decoded messages with the reference")
-
- # Compare bursts
- assert(msg_l12trx_dec.burst == msg_l12trx_ref.burst)
- assert(msg_trx2l1_dec.burst == msg_trx2l1_ref.burst)
- assert(msg_trx2l1_legacy_dec.burst == msg_trx2l1_ref.burst)
-
- log.info("Compare bursts: OK")
-
- # Compare both parsed messages with the reference data
- assert(msg_l12trx_dec.fn == msg_l12trx_ref.fn)
- assert(msg_trx2l1_dec.fn == msg_trx2l1_ref.fn)
- assert(msg_l12trx_dec.tn == msg_l12trx_ref.tn)
- assert(msg_trx2l1_dec.tn == msg_trx2l1_ref.tn)
-
- log.info("Compare FN / TN: OK")
-
- # Compare message specific parts
- assert(msg_trx2l1_dec.rssi == msg_trx2l1_ref.rssi)
- assert(msg_l12trx_dec.pwr == msg_l12trx_ref.pwr)
- assert(msg_trx2l1_dec.toa256 == msg_trx2l1_ref.toa256)
-
- log.info("Compare message specific data: OK")
-
- # Bit conversation test
- usbits_ref = list(range(0, 256))
- sbits_ref = list(range(-127, 128))
-
- # Test both usbit2sbit() and sbit2usbit()
- sbits = DATAMSG.usbit2sbit(usbits_ref)
- usbits = DATAMSG.sbit2usbit(sbits)
- assert(usbits[:255] == usbits_ref[:255])
- assert(usbits[255] == 254)
-
- log.info("Check both usbit2sbit() and sbit2usbit(): OK")
-
- # Test both sbit2ubit() and ubit2sbit()
- ubits = DATAMSG.sbit2ubit(sbits_ref)
- assert(ubits == ([1] * 127 + [0] * 128))
-
- sbits = DATAMSG.ubit2sbit(ubits)
- assert(sbits == ([-127] * 127 + [127] * 128))
-
- log.info("Check both sbit2ubit() and ubit2sbit(): OK")
-
- # Test message transformation
- msg_l12trx_dec = msg_trx2l1_ref.gen_l12trx()
- msg_trx2l1_dec = msg_l12trx_ref.gen_trx2l1()
-
- assert(msg_l12trx_dec.fn == msg_trx2l1_ref.fn)
- assert(msg_l12trx_dec.tn == msg_trx2l1_ref.tn)
-
- assert(msg_trx2l1_dec.fn == msg_l12trx_ref.fn)
- assert(msg_trx2l1_dec.tn == msg_l12trx_ref.tn)
-
- assert(msg_l12trx_dec.burst == DATAMSG.sbit2ubit(msg_trx2l1_ref.burst))
- assert(msg_trx2l1_dec.burst == DATAMSG.ubit2sbit(msg_l12trx_ref.burst))
-
- log.info("Check L12TRX <-> TRX2L1 type transformations: OK")
-
- # Test header version coding
- for ver in DATAMSG.known_versions:
- # Create messages of both types
- msg_l12trx = DATAMSG_L12TRX(ver = ver)
- msg_trx2l1 = DATAMSG_TRX2L1(ver = ver)
-
- # Randomize message specific headers
- msg_l12trx.rand_hdr()
- msg_trx2l1.rand_hdr()
-
- # Randomize bursts
- msg_l12trx.rand_burst()
- msg_trx2l1.rand_burst()
-
- # Encode DATA messages
- msg_l12trx_enc = msg_l12trx.gen_msg()
- msg_trx2l1_enc = msg_trx2l1.gen_msg()
-
- # Parse generated DATA messages
- msg_l12trx_dec = DATAMSG_L12TRX()
- msg_trx2l1_dec = DATAMSG_TRX2L1()
- msg_l12trx_dec.parse_msg(msg_l12trx_enc)
- msg_trx2l1_dec.parse_msg(msg_trx2l1_enc)
-
- # Match the header version
- assert(msg_l12trx_dec.ver == ver)
- assert(msg_trx2l1_dec.ver == ver)
-
- # Match common TDMA fields
- assert(msg_l12trx_dec.tn == msg_l12trx.tn)
- assert(msg_trx2l1_dec.fn == msg_trx2l1.fn)
-
- # Match version specific fields
- if msg_trx2l1.ver >= 0x01:
- assert(msg_trx2l1_dec.nope_ind == msg_trx2l1.nope_ind)
- assert(msg_trx2l1_dec.mod_type == msg_trx2l1.mod_type)
- assert(msg_trx2l1_dec.tsc_set == msg_trx2l1.tsc_set)
- assert(msg_trx2l1_dec.tsc == msg_trx2l1.tsc)
- assert(msg_trx2l1_dec.ci == msg_trx2l1.ci)
-
- log.info("Check header version %u coding: OK" % ver)
-
- # Compare bursts
- assert(msg_l12trx_dec.burst == msg_l12trx.burst)
- assert(msg_trx2l1_dec.burst == msg_trx2l1.burst)
-
- msg_trx2l1_gen = msg_l12trx.gen_trx2l1()
- msg_l12trx_gen = msg_trx2l1.gen_l12trx()
-
- assert(msg_trx2l1_gen is not None)
- assert(msg_l12trx_gen is not None)
-
- # Match the header version
- assert(msg_trx2l1_gen.ver == ver)
- assert(msg_l12trx_gen.ver == ver)
-
- # Match common TDMA fields
- assert(msg_trx2l1_gen.tn == msg_l12trx.tn)
- assert(msg_l12trx_gen.fn == msg_trx2l1.fn)
-
- log.info("Verify version %u direct transformation: OK" % ver)
-
- # Verify NOPE indication coding
- if msg_trx2l1.ver >= 0x01:
- msg_trx2l1 = DATAMSG_TRX2L1(ver = ver)
- msg_trx2l1.nope_ind = True
- msg_trx2l1.rand_hdr()
-
- msg_trx2l1_dec = DATAMSG_TRX2L1()
- msg_trx2l1_dec.parse_msg(msg_trx2l1.gen_msg())
-
- assert(msg_trx2l1.nope_ind == msg_trx2l1_dec.nope_ind)
- assert(msg_trx2l1.burst == msg_trx2l1_dec.burst)
-
- log.info("Verify version %u NOPE indication coding: OK" % ver)
diff --git a/src/target/trx_toolkit/fake_pm.py b/src/target/trx_toolkit/fake_pm.py
index 51dc0576..205596a9 100644
--- a/src/target/trx_toolkit/fake_pm.py
+++ b/src/target/trx_toolkit/fake_pm.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -17,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
@@ -64,14 +59,19 @@ class FakePM:
def rssi_trx(self):
return randint(self.trx_min, self.trx_max)
- def measure(self, freq):
+ def measure(self, freq, fn = None):
# Iterate over all known transceivers
for trx in self.trx_list:
if not trx.running:
continue
+ # FIXME: we need to know current TDMA frame number here,
+ # because some transceivers may use frequency hopping
+ if trx.fh is not None and fn is None:
+ continue
+
# Match by given frequency
- if trx.tx_freq == freq:
+ if trx.get_tx_freq(fn) == freq:
return self.rssi_trx
return self.rssi_noise
diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py
index 8beee6e9..0daecb40 100755
--- a/src/target/trx_toolkit/fake_trx.py
+++ b/src/target/trx_toolkit/fake_trx.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TRX Toolkit
# Virtual Um-interface (fake transceiver)
#
-# (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
@@ -17,12 +17,8 @@
# 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>")]
+APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
import logging as log
import signal
@@ -106,16 +102,32 @@ class FakeTRX(Transceiver):
"""
+ NOMINAL_TX_POWER_DEFAULT = 50 # dBm
+ TX_ATT_DEFAULT = 0 # dB
+ PATH_LOSS_DEFAULT = 110 # dB
+
TOA256_BASE_DEFAULT = 0
- RSSI_BASE_DEFAULT = -60
CI_BASE_DEFAULT = 90
+ # Default values for NOPE / IDLE indications
+ TOA256_NOISE_DEFAULT = 0
+ RSSI_NOISE_DEFAULT = -110
+ CI_NOISE_DEFAULT = -30
+
def __init__(self, *trx_args, **trx_kwargs):
Transceiver.__init__(self, *trx_args, **trx_kwargs)
+ # fake RSSI is disabled by default, only enabled through TRXC FAKE_RSSI.
+ # When disabled, RSSI is calculated based on Tx power and Rx path loss
+ self.fake_rssi_enabled = False
+
+ self.rf_muted = False
+
# Actual ToA, RSSI, C/I, TA values
+ self.tx_power_base = self.NOMINAL_TX_POWER_DEFAULT
+ self.tx_att_base = self.TX_ATT_DEFAULT
self.toa256_base = self.TOA256_BASE_DEFAULT
- self.rssi_base = self.RSSI_BASE_DEFAULT
+ self.rssi_base = self.NOMINAL_TX_POWER_DEFAULT - self.TX_ATT_DEFAULT - self.PATH_LOSS_DEFAULT
self.ci_base = self.CI_BASE_DEFAULT
self.ta = 0
@@ -131,7 +143,7 @@ class FakeTRX(Transceiver):
@property
def toa256(self):
# Check if randomization is required
- if self.toa256_rand_threshold is 0:
+ if self.toa256_rand_threshold == 0:
return self.toa256_base
# Generate a random ToA value in required range
@@ -142,7 +154,7 @@ class FakeTRX(Transceiver):
@property
def rssi(self):
# Check if randomization is required
- if self.rssi_rand_threshold is 0:
+ if self.rssi_rand_threshold == 0:
return self.rssi_base
# Generate a random RSSI value in required range
@@ -151,9 +163,13 @@ class FakeTRX(Transceiver):
return random.randint(rssi_min, rssi_max)
@property
+ def tx_power(self):
+ return self.tx_power_base - self.tx_att_base
+
+ @property
def ci(self):
# Check if randomization is required
- if self.ci_rand_threshold is 0:
+ if self.ci_rand_threshold == 0:
return self.ci_base
# Generate a random C/I value in required range
@@ -165,7 +181,7 @@ class FakeTRX(Transceiver):
# Returns: True - drop, False - keep
def sim_burst_drop(self, msg):
# Check if dropping is required
- if self.burst_drop_amount is 0:
+ if self.burst_drop_amount == 0:
return False
if msg.fn % self.burst_drop_period == 0:
@@ -177,9 +193,6 @@ class FakeTRX(Transceiver):
return False
def _handle_data_msg_v1(self, src_msg, msg):
- # TODO: NOPE indications are not (yet) supported
- msg.nope_ind = False
-
# C/I (Carrier-to-Interference ratio)
msg.ci = self.ci
@@ -196,31 +209,51 @@ class FakeTRX(Transceiver):
msg.tsc_set = 0
msg.tsc = 0
- # Takes (partially initialized) TRX2L1 message,
+ # Takes (partially initialized) TRXD Rx message,
# simulates RF path parameters (such as RSSI),
# and sends towards the L1
- def send_data_msg(self, src_trx, src_msg, msg):
- # Override header version
- msg.ver = self.data_if._hdr_ver
+ def handle_data_msg(self, src_trx, src_msg, msg):
+ if self.rf_muted:
+ msg.nope_ind = True
+ elif not msg.nope_ind:
+ # Path loss simulation
+ msg.nope_ind = self.sim_burst_drop(msg)
+ if msg.nope_ind:
+ # Before TRXDv1, we simply drop the message
+ if msg.ver < 0x01:
+ del msg
+ return
+
+ # Since TRXDv1, we should send a NOPE.ind
+ del msg.burst # burst bits are omited
+ msg.burst = None
+
+ # TODO: shoud we make these values configurable?
+ msg.toa256 = self.TOA256_NOISE_DEFAULT
+ msg.rssi = self.RSSI_NOISE_DEFAULT
+ msg.ci = self.CI_NOISE_DEFAULT
+
+ self.data_if.send_msg(msg)
+ return
# Complete message header
msg.toa256 = self.toa256
- msg.rssi = self.rssi
+
+ # Apply RSSI based on transmitter:
+ if not self.fake_rssi_enabled:
+ msg.rssi = src_trx.tx_power - src_msg.pwr - self.PATH_LOSS_DEFAULT
+ else: # Apply fake RSSI
+ msg.rssi = self.rssi
# Version specific fields
if msg.ver >= 0x01:
self._handle_data_msg_v1(src_msg, msg)
# Apply optional Timing Advance
- if src_trx.ta is not 0:
+ if src_trx.ta != 0:
msg.toa256 -= src_trx.ta * 256
- # Path loss simulation
- if self.sim_burst_drop(msg):
- return
-
- # TODO: make legacy mode configurable (via argv?)
- self.data_if.send_msg(msg, legacy = True)
+ Transceiver.handle_data_msg(self, msg)
# Simulation specific CTRL command handler
def ctrl_cmd_handler(self, request):
@@ -257,9 +290,15 @@ class FakeTRX(Transceiver):
elif self.ctrl_if.verify_cmd(request, "FAKE_RSSI", 2):
log.debug("(%s) Recv FAKE_RSSI cmd" % self)
+ # Use negative threshold to disable fake_rssi if previously enabled:
+ if int(request[2]) < 0:
+ self.fake_rssi_enabled = False
+ return 0
+
# Parse and apply both base and threshold
self.rssi_base = int(request[1])
self.rssi_rand_threshold = int(request[2])
+ self.fake_rssi_enabled = True
return 0
# RSSI simulation
@@ -331,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
@@ -358,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)
+ 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 is 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
@@ -396,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'
@@ -405,7 +450,7 @@ class Application(ApplicationBase):
def run(self):
# Compose list of to be monitored sockets
sock_list = []
- for trx in self.trx_list:
+ for trx in self.trx_list.trx_list:
sock_list.append(trx.ctrl_if.sock)
sock_list.append(trx.data_if.sock)
@@ -415,7 +460,7 @@ class Application(ApplicationBase):
r_event, _, _ = select.select(sock_list, [], [])
# Iterate over all transceivers
- for trx in self.trx_list:
+ for trx in self.trx_list.trx_list:
# DATA interface
if trx.data_if.sock in r_event:
msg = trx.recv_data_msg()
@@ -440,7 +485,7 @@ class Application(ApplicationBase):
@staticmethod
def trx_def(val):
try:
- result = re.match("(.+@)?(.+):([0-9]+)(\/[0-9]+)?", val)
+ result = re.match(r"(.+@)?(.+):([0-9]+)(/[0-9]+)?", val)
(name, addr, port, idx) = result.groups()
except:
raise argparse.ArgumentTypeError("Invalid TRX definition: %s" % val)
diff --git a/src/target/trx_toolkit/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py
index 71f43a7f..5f59ba6f 100644
--- a/src/target/trx_toolkit/gsm_shared.py
+++ b/src/target/trx_toolkit/gsm_shared.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
-# Common GSM constants
+# Common GSM constants and helpers
#
-# (C) 2018-2019 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
#
# All Rights Reserved
#
@@ -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
@@ -29,8 +25,8 @@ GSM_SUPERFRAME = 26 * 51
GSM_HYPERFRAME = 2048 * GSM_SUPERFRAME
# Burst length
-GSM_BURST_LEN = 148
-EDGE_BURST_LEN = GSM_BURST_LEN * 3
+GMSK_BURST_LEN = 148
+EDGE_BURST_LEN = GMSK_BURST_LEN * 3
class BurstType(Enum):
""" Burst types defined in 3GPP TS 45.002 """
@@ -106,3 +102,73 @@ class TrainingSeqGMSK(Enum):
return ts
return None
+
+class HoppingParams:
+ """ Hopping sequence generation as per 3GPP TS 45.002, section 6.2.3.
+
+ Based on firmware/layer1/rfch.c:rfch_hop_seq_gen() by Sylvain Munaut.
+
+ """
+
+ # Magic numbers for pseudo-random hopping sequence generation
+ RNTABLE = [
+ 48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
+ 0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
+ 101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
+ 80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
+ 55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
+ 87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
+ 82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
+ 77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
+ 117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
+ 16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
+ 91, 15, 21, 24, 46, 39, 93, 105, 65, 70,
+ 125, 99, 17, 123,
+ ]
+
+ def __init__(self, hsn, maio, ma):
+ # Make sure MA is not empty
+ ma_len = len(ma)
+ if ma_len == 0: # TODO: or rather > 1?
+ raise ValueError("Mobile Allocation is empty")
+
+ self.hsn = hsn
+ self.maio = maio
+ self.ma = ma
+
+ # Pre-calculate 2 ** NBIN in advance
+ self._pnm = (ma_len >> 0) | (ma_len >> 1) \
+ | (ma_len >> 2) | (ma_len >> 3) \
+ | (ma_len >> 4) | (ma_len >> 5) \
+ | (ma_len >> 6)
+
+ def __str__(self):
+ fmt = "hsn=%u, maio=%u, ma_len=%u"
+ return fmt % (self.hsn, self.maio, len(self.ma))
+
+ @staticmethod
+ def fn2gsm_time(fn):
+ t1 = fn // (26 * 51)
+ t2 = fn % 26
+ t3 = fn % 51
+ tc = (fn // 51) % 8
+ return (t1, t2, t3, tc)
+
+ # Resolve current ARFCN using the given TDMA frame number
+ def resolve(self, fn):
+ # Cyclic hopping
+ if self.hsn == 0:
+ mai = (fn + self.maio) % len(self.ma)
+ return self.ma[mai]
+
+ # Pseudo random hopping
+ (t1, t2, t3, tc) = self.fn2gsm_time(fn)
+ ma_len = len(self.ma)
+
+ rn_idx = (self.hsn ^ (t1 & 63)) + t3
+ m = t2 + self.RNTABLE[rn_idx]
+ mp = m & self._pnm
+
+ s = mp if mp < ma_len else (mp + t3 & self._pnm) % ma_len
+ mai = (s + self.maio) % ma_len
+ return self.ma[mai]
diff --git a/src/target/trx_toolkit/rand_burst_gen.py b/src/target/trx_toolkit/rand_burst_gen.py
index be4b6961..c474c759 100644
--- a/src/target/trx_toolkit/rand_burst_gen.py
+++ b/src/target/trx_toolkit/rand_burst_gen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -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
@@ -57,8 +53,7 @@ class RandBurstGen:
buf += [0] * 3
# Random data 1 / 2
- for i in range(0, 57):
- buf.append(random.randint(0, 1))
+ buf += [random.randint(0, 1) for _ in range(57)]
# Steal flag 1 / 2
buf.append(random.randint(0, 1))
@@ -72,8 +67,7 @@ class RandBurstGen:
buf.append(random.randint(0, 1))
# Random data 2 / 2
- for i in range(0, 57):
- buf.append(random.randint(0, 1))
+ buf += [random.randint(0, 1) for _ in range(57)]
# Tailing bits
buf += [0] * 3
@@ -82,7 +76,7 @@ class RandBurstGen:
# Generate a frequency correction burst
def gen_fb(self):
- return [0] * GSM_BURST_LEN
+ return [0] * GMSK_BURST_LEN
# Generate a synchronization burst
def gen_sb(self, tsc = None):
@@ -92,8 +86,7 @@ class RandBurstGen:
buf += [0] * 3
# Random data 1 / 2
- for i in range(0, 39):
- buf.append(random.randint(0, 1))
+ buf += [random.randint(0, 1) for _ in range(39)]
# Training sequence
if tsc is None:
@@ -101,8 +94,7 @@ class RandBurstGen:
buf += tsc.seq
# Random data 2 / 2
- for i in range(0, 39):
- buf.append(random.randint(0, 1))
+ buf += [random.randint(0, 1) for _ in range(39)]
# Tailing bits
buf += [0] * 3
@@ -126,8 +118,7 @@ class RandBurstGen:
buf += tsc.seq
# Random data
- for i in range(0, 36):
- buf.append(random.randint(0, 1))
+ buf += [random.randint(0, 1) for _ in range(36)]
# Tailing bits
buf += [0] * 3
diff --git a/src/target/trx_toolkit/test_codec.py b/src/target/trx_toolkit/test_codec.py
new file mode 100644
index 00000000..5049f525
--- /dev/null
+++ b/src/target/trx_toolkit/test_codec.py
@@ -0,0 +1,592 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+'''
+Unit tests for declarative message codec.
+'''
+
+# (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+# Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+#
+# All Rights Reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU 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.
+
+import unittest
+import struct
+import codec
+
+class TestField(codec.Field):
+ DEF_PARAMS = { 'key' : 0xde }
+ DEF_LEN = 4
+
+ @staticmethod
+ def xor(data: bytes, key: int = 0x00):
+ return bytes([x ^ key for x in data])
+
+ def _from_bytes(self, vals: dict, data: bytes) -> None:
+ vals[self.name] = self.xor(data, self.p['key'])
+
+ def _to_bytes(self, vals: dict) -> bytes:
+ return self.xor(self.get_val(vals), self.p['key'])
+
+class Field(unittest.TestCase):
+ MAGIC = b'\xde\xad\xbe\xef'
+
+ def test_to_bytes(self):
+ vals = { 'magic' : self.MAGIC, 'other' : 'unrelated' }
+ encoded_de = TestField.xor(self.MAGIC, 0xde)
+ encoded_88 = TestField.xor(self.MAGIC, 0x88)
+
+ with self.subTest('default length=4, default key=0xde'):
+ field = TestField('magic')
+ self.assertEqual(field.to_bytes(vals), encoded_de)
+
+ with self.subTest('default length=4, different key=0x88'):
+ field = TestField('magic', key=0x88)
+ self.assertEqual(field.to_bytes(vals), encoded_88)
+
+ with self.subTest('different length=2, default key=0xde'):
+ field = TestField('magic', len=2)
+ vals['magic'] = vals['magic'][:2]
+ self.assertEqual(field.to_bytes(vals), encoded_de[:2])
+
+ with self.subTest('EncodeError due to length mismatch'):
+ field = TestField('magic', len=8)
+ with self.assertRaises(codec.EncodeError):
+ field.to_bytes(vals)
+
+ def test_from_bytes(self):
+ encoded_de = TestField.xor(self.MAGIC, 0xde) + b'\xff' * 60
+ encoded_88 = TestField.xor(self.MAGIC, 0x88) + b'\xff' * 60
+ vals = { 'magic' : 'overrien', 'other' : 'unchanged' }
+
+ with self.subTest('default length=4, default key=0xde'):
+ field = TestField('magic')
+ offset = field.from_bytes(vals, encoded_de)
+ self.assertEqual(vals['other'], 'unchanged')
+ self.assertEqual(vals['magic'], self.MAGIC)
+ self.assertEqual(offset, len(self.MAGIC))
+
+ with self.subTest('default length=4, different key=0x88'):
+ field = TestField('magic', key=0x88)
+ offset = field.from_bytes(vals, encoded_88)
+ self.assertEqual(vals['other'], 'unchanged')
+ self.assertEqual(vals['magic'], self.MAGIC)
+ self.assertEqual(offset, len(self.MAGIC))
+
+ with self.subTest('different length=2, default key=0xde'):
+ field = TestField('magic', len=2)
+ offset = field.from_bytes(vals, encoded_de)
+ self.assertEqual(vals['other'], 'unchanged')
+ self.assertEqual(vals['magic'], self.MAGIC[:2])
+ self.assertEqual(offset, 2)
+
+ with self.subTest('full length, different key=0x88'):
+ field = TestField('magic', len=0, key=0x88)
+ offset = field.from_bytes(vals, encoded_88)
+ self.assertEqual(vals['other'], 'unchanged')
+ self.assertEqual(vals['magic'], self.MAGIC + b'\x77' * 60)
+ self.assertEqual(offset, len(encoded_88))
+
+ with self.subTest('DecodeError due to short read'):
+ field = TestField('magic', len=4)
+ with self.assertRaises(codec.DecodeError):
+ field.from_bytes(vals, b'\x00')
+
+ def test_get_pres(self):
+ vals = { 'magic' : self.MAGIC }
+
+ with self.subTest('to_bytes() for a non-existing field'):
+ field = TestField('not-there')
+ with self.assertRaises(KeyError):
+ field.to_bytes(vals)
+
+ with self.subTest('to_bytes() for a field with get_pres()'):
+ field = TestField('magic', key=0x00)
+ field.get_pres = lambda v: not v['omit']
+
+ data = field.to_bytes({ **vals, 'omit' : False })
+ self.assertEqual(data, self.MAGIC)
+
+ data = field.to_bytes({ **vals, 'omit' : True })
+ self.assertEqual(data, b'')
+
+ with self.subTest('from_bytes() for a field with get_pres()'):
+ field = TestField('magic', key=0x00)
+ field.get_pres = lambda v: not v['omit']
+
+ vals = { 'omit' : False }
+ offset = field.from_bytes(vals, self.MAGIC)
+ self.assertEqual(vals['magic'], self.MAGIC)
+ self.assertEqual(offset, len(self.MAGIC))
+
+ vals = { 'omit' : True }
+ offset = field.from_bytes(vals, self.MAGIC)
+ self.assertFalse('magic' in vals)
+ self.assertEqual(offset, 0)
+
+ def test_get_len(self):
+ vals = { 'len' : 32, 'unrelated' : 'foo' }
+
+ field = TestField('magic', key=0x00)
+ field.get_len = lambda v, _: v['len']
+
+ with self.subTest('not enough octets in the buffer: 16 < 32'):
+ with self.assertRaises(codec.DecodeError):
+ field.from_bytes(vals, b'\xff' * 16)
+
+ with self.subTest('more than enough octets in the buffer'):
+ offset = field.from_bytes(vals, b'\xff' * 64)
+ self.assertEqual(vals['magic'], b'\xff' * 32)
+ self.assertEqual(offset, 32)
+
+ with self.subTest('length field does not exist'):
+ with self.assertRaises(KeyError):
+ field.from_bytes({ }, b'\xff' * 64)
+
+ def test_get_val(self):
+ field = TestField('magic', key=0x00, len=0)
+ field.get_val = lambda v: v.get('val', self.MAGIC)
+
+ with self.subTest('value is present in the dict'):
+ data = field.to_bytes({ 'val' : b'\xd0\xde' })
+ self.assertEqual(data, b'\xd0\xde')
+
+ with self.subTest('value is not present in the dict'):
+ data = field.to_bytes({ })
+ self.assertEqual(data, self.MAGIC)
+
+class Buf(unittest.TestCase):
+ MAGIC = b'\xde\xad' * 4
+
+ def test_to_bytes(self):
+ vals = { 'buf' : self.MAGIC }
+
+ with self.subTest('with no length constraints'):
+ field = codec.Buf('buf') # default: len=0
+ self.assertEqual(field.to_bytes(vals), self.MAGIC)
+
+ with self.subTest('with length constraints'):
+ field = codec.Buf('buf', len=len(self.MAGIC))
+ self.assertEqual(field.to_bytes(vals), self.MAGIC)
+
+ with self.subTest('EncodeError due to length mismatch'):
+ field = codec.Buf('buf', len=4)
+ with self.assertRaises(codec.EncodeError):
+ field.to_bytes(vals)
+
+ def test_from_bytes(self):
+ vals = { }
+
+ with self.subTest('with no length constraints'):
+ field = codec.Buf('buf') # default: len=0
+ offset = field.from_bytes(vals, self.MAGIC)
+ self.assertEqual(vals['buf'], self.MAGIC)
+ self.assertEqual(offset, len(self.MAGIC))
+
+ with self.subTest('with length constraints'):
+ field = codec.Buf('buf', len=2)
+ offset = field.from_bytes(vals, self.MAGIC)
+ self.assertEqual(vals['buf'], self.MAGIC[:2])
+ self.assertEqual(offset, len(self.MAGIC[:2]))
+
+ with self.subTest('DecodeError due to not enough bytes'):
+ field = codec.Buf('buf', len=64)
+ with self.assertRaises(codec.DecodeError):
+ field.from_bytes(vals, self.MAGIC)
+
+class Spare(unittest.TestCase):
+ # Fixed length with custom filler
+ SAA = codec.Spare('pad', len=4, filler=b'\xaa')
+ # Auto-calculated length with custom filler
+ SFF = codec.Spare('pad', filler=b'\xff')
+ SFF.get_len = lambda v, _: v['len']
+ # Fixed length with default filler
+ S00 = codec.Spare('pad', len=2)
+
+ def test_to_bytes(self):
+ self.assertEqual(self.SFF.to_bytes({ 'len' : 8 }), b'\xff' * 8)
+ self.assertEqual(self.SAA.to_bytes({ }), b'\xaa' * 4)
+ self.assertEqual(self.S00.to_bytes({ }), b'\x00' * 2)
+
+ def test_from_bytes(self):
+ with self.assertRaises(codec.DecodeError):
+ self.S00.from_bytes({ }, b'\x00') # Short read
+ self.assertEqual(self.SFF.from_bytes({ 'len' : 8 }, b'\xff' * 8), 8)
+ self.assertEqual(self.SAA.from_bytes({ }, b'\xaa' * 64), 4)
+ self.assertEqual(self.S00.from_bytes({ }, b'\x00' * 64), 2)
+
+class Uint(unittest.TestCase):
+ def _test_uint(self, field, fmt, vals):
+ for i in vals:
+ with self.subTest('to_bytes()'):
+ val = field.to_bytes({ field.name : i })
+ self.assertEqual(val, struct.pack(fmt, i))
+
+ with self.subTest('from_bytes()'):
+ data, parsed = struct.pack(fmt, i), { }
+ offset = field.from_bytes(parsed, data)
+ self.assertEqual(offset, len(data))
+ self.assertEqual(parsed[field.name], i)
+
+ def test_uint8(self):
+ self._test_uint(codec.Uint('foo'), 'B', range(2 ** 8))
+
+ def test_int8(self):
+ self._test_uint(codec.Int('foo'), 'b', range(-128, 128))
+
+ def test_uint16(self):
+ vals = (0, 65, 128, 255, 512, 1023, 2 ** 16 - 1)
+ self._test_uint(codec.Uint16BE('foo'), '>H', vals)
+ self._test_uint(codec.Uint16LE('foo'), '<H', vals)
+
+ def test_int16(self):
+ vals = (-32767, -16384, 0, 16384, 32767)
+ self._test_uint(codec.Int16BE('foo'), '>h', vals)
+ self._test_uint(codec.Int16LE('foo'), '<h', vals)
+
+ def test_uint32(self):
+ vals = (0, 33, 255, 1024, 1337, 4099, 2 ** 32 - 1)
+ self._test_uint(codec.Uint32BE('foo'), '>I', vals)
+ self._test_uint(codec.Uint32LE('foo'), '<I', vals)
+
+ def test_int32(self):
+ vals = (-2147483647, 0, 2147483647)
+ self._test_uint(codec.Int32BE('foo'), '>i', vals)
+ self._test_uint(codec.Int32LE('foo'), '<i', vals)
+
+ def test_offset_mult(self):
+ with self.subTest('encode / decode with offset=5'):
+ field = codec.Uint('foo', offset=5)
+
+ self.assertEqual(field.to_bytes({ 'foo' : 10 }), b'\x05')
+ self.assertEqual(field.to_bytes({ 'foo' : 5 }), b'\x00')
+
+ vals = { 'foo' : 'overriden' }
+ field.from_bytes(vals, b'\xff')
+ self.assertEqual(vals['foo'], 260)
+ field.from_bytes(vals, b'\x00')
+ self.assertEqual(vals['foo'], 5)
+
+ with self.subTest('encode / decode with mult=2'):
+ field = codec.Uint('foo', mult=2)
+
+ self.assertEqual(field.to_bytes({ 'foo' : 0 }), b'\x00')
+ self.assertEqual(field.to_bytes({ 'foo' : 3 }), b'\x01')
+ self.assertEqual(field.to_bytes({ 'foo' : 32 }), b'\x10')
+ self.assertEqual(field.to_bytes({ 'foo' : 64 }), b'\x20')
+
+ vals = { 'foo' : 'overriden' }
+ field.from_bytes(vals, b'\x00')
+ self.assertEqual(vals['foo'], 0 * 2)
+ field.from_bytes(vals, b'\x0f')
+ self.assertEqual(vals['foo'], 15 * 2)
+ field.from_bytes(vals, b'\xff')
+ self.assertEqual(vals['foo'], 255 * 2)
+
+class BitFieldSet(unittest.TestCase):
+ S16 = codec.BitFieldSet(set=(
+ codec.BitField('f4a', bl=4),
+ codec.BitField('f8', bl=8),
+ codec.BitField('f4b', bl=4),
+ ))
+
+ S8M = codec.BitFieldSet(order='msb', set=(
+ codec.BitField('f4', bl=4),
+ codec.BitField('f1', bl=1),
+ codec.BitField('f3', bl=3),
+ ))
+
+ S8L = codec.BitFieldSet(order='lsb', set=(
+ codec.BitField('f4', bl=4),
+ codec.BitField('f1', bl=1),
+ codec.BitField('f3', bl=3),
+ ))
+
+ S8V = codec.BitFieldSet(set=(
+ codec.BitField('f4', bl=4, val=2),
+ codec.BitField('f1', bl=1, val=0),
+ codec.BitField('f3', bl=3),
+ ))
+
+ S8P = codec.BitFieldSet(set=(
+ codec.BitField.Spare(bl=4),
+ codec.BitField('f4', bl=4),
+ ))
+
+ @staticmethod
+ def from_bytes(s: codec.BitFieldSet, data: bytes) -> dict:
+ vals = { }
+ s.from_bytes(vals, data)
+ return vals
+
+ def test_len_auto(self):
+ with self.subTest('1 + 2 = 3 bits => 1 octet (with padding)'):
+ s = codec.BitFieldSet(set=(
+ codec.BitField('f1', bl=1),
+ codec.BitField('f2', bl=2),
+ ))
+ self.assertEqual(s.len, 1)
+
+ with self.subTest('4 + 2 + 2 = 8 bits => 1 octet'):
+ s = codec.BitFieldSet(set=(
+ codec.BitField('f4', bl=4),
+ codec.BitField('f2a', bl=2),
+ codec.BitField('f2b', bl=2),
+ ))
+ self.assertEqual(s.len, 1)
+
+ with self.subTest('12 + 4 + 2 = 18 bits => 3 octets (with padding)'):
+ s = codec.BitFieldSet(set=(
+ codec.BitField('f12', bl=12),
+ codec.BitField('f4', bl=4),
+ codec.BitField('f2', bl=2),
+ ))
+ self.assertEqual(s.len, 3)
+
+ def test_overflow(self):
+ with self.assertRaises(codec.ProtocolError):
+ s = codec.BitFieldSet(len=1, set=(
+ codec.BitField('f6', bl=6),
+ codec.BitField('f4', bl=4),
+ ))
+
+ def test_offset_mask(self):
+ calc = lambda s: [(f.name, f.offset, f.mask) for f in s._fields]
+
+ with self.subTest('16 bit total (MSB): f4a + f8 + f4b'):
+ om = [('f4a', 8 + 4, 0x0f), ('f8', 4, 0xff), ('f4b', 0, 0x0f)]
+ self.assertEqual(len(self.S16._fields), 3)
+ self.assertEqual(calc(self.S16), om)
+
+ with self.subTest('8 bit total (MSB): f4 + f1 + f3'):
+ om = [('f4', 1 + 3, 0x0f), ('f1', 3, 0x01), ('f3', 0, 0x07)]
+ self.assertEqual(len(self.S8M._fields), 3)
+ self.assertEqual(calc(self.S8M), om)
+
+ with self.subTest('8 bit total (LSB): f4 + f1 + f3'):
+ om = [('f3', 1 + 4, 0x07), ('f1', 4, 0x01), ('f4', 0, 0x0f)]
+ self.assertEqual(len(self.S8L._fields), 3)
+ self.assertEqual(calc(self.S8L), om)
+
+ with self.subTest('8 bit total (LSB): s4 + f4'):
+ om = [(None, 4, 0x0f), ('f4', 0, 0x0f)]
+ self.assertEqual(len(self.S8P._fields), 2)
+ self.assertEqual(calc(self.S8P), om)
+
+ def test_to_bytes(self):
+ with self.subTest('16 bit total (MSB): f4a + f8 + f4b'):
+ vals = { 'f4a' : 0x0f, 'f8' : 0xff, 'f4b' : 0x0f }
+ self.assertEqual(self.S16.to_bytes(vals), b'\xff\xff')
+ vals = { 'f4a' : 0x00, 'f8' : 0x00, 'f4b' : 0x00 }
+ self.assertEqual(self.S16.to_bytes(vals), b'\x00\x00')
+ vals = { 'f4a' : 0x0f, 'f8' : 0x00, 'f4b' : 0x0f }
+ self.assertEqual(self.S16.to_bytes(vals), b'\xf0\x0f')
+ vals = { 'f4a' : 0x00, 'f8' : 0xff, 'f4b' : 0x00 }
+ self.assertEqual(self.S16.to_bytes(vals), b'\x0f\xf0')
+
+ with self.subTest('8 bit total (MSB): f4 + f1 + f3'):
+ vals = { 'f4' : 0x0f, 'f1' : 0x01, 'f3' : 0x07 }
+ self.assertEqual(self.S8M.to_bytes(vals), b'\xff')
+ vals = { 'f4' : 0x00, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.S8M.to_bytes(vals), b'\x00')
+ vals = { 'f4' : 0x0f, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.S8M.to_bytes(vals), b'\xf0')
+
+ with self.subTest('8 bit total (LSB): f4 + f1 + f3'):
+ vals = { 'f4' : 0x0f, 'f1' : 0x01, 'f3' : 0x07 }
+ self.assertEqual(self.S8L.to_bytes(vals), b'\xff')
+ vals = { 'f4' : 0x00, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.S8L.to_bytes(vals), b'\x00')
+ vals = { 'f4' : 0x0f, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.S8L.to_bytes(vals), b'\x0f')
+
+ def test_from_bytes(self):
+ pad = b'\xff' * 64
+
+ with self.subTest('16 bit total (MSB): f4a + f8 + f4b'):
+ vals = { 'f4a' : 0x0f, 'f8' : 0xff, 'f4b' : 0x0f }
+ self.assertEqual(self.from_bytes(self.S16, b'\xff\xff' + pad), vals)
+ vals = { 'f4a' : 0x00, 'f8' : 0x00, 'f4b' : 0x00 }
+ self.assertEqual(self.from_bytes(self.S16, b'\x00\x00' + pad), vals)
+ vals = { 'f4a' : 0x0f, 'f8' : 0x00, 'f4b' : 0x0f }
+ self.assertEqual(self.from_bytes(self.S16, b'\xf0\x0f' + pad), vals)
+ vals = { 'f4a' : 0x00, 'f8' : 0xff, 'f4b' : 0x00 }
+ self.assertEqual(self.from_bytes(self.S16, b'\x0f\xf0' + pad), vals)
+
+ with self.subTest('8 bit total (MSB): f4 + f1 + f3'):
+ vals = { 'f4' : 0x0f, 'f1' : 0x01, 'f3' : 0x07 }
+ self.assertEqual(self.from_bytes(self.S8M, b'\xff' + pad), vals)
+ vals = { 'f4' : 0x00, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.from_bytes(self.S8M, b'\x00' + pad), vals)
+ vals = { 'f4' : 0x0f, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.from_bytes(self.S8M, b'\xf0' + pad), vals)
+
+ with self.subTest('8 bit total (LSB): f4 + f1 + f3'):
+ vals = { 'f4' : 0x0f, 'f1' : 0x01, 'f3' : 0x07 }
+ self.assertEqual(self.from_bytes(self.S8L, b'\xff' + pad), vals)
+ vals = { 'f4' : 0x00, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.from_bytes(self.S8L, b'\x00' + pad), vals)
+ vals = { 'f4' : 0x0f, 'f1' : 0x00, 'f3' : 0x00 }
+ self.assertEqual(self.from_bytes(self.S8L, b'\x0f' + pad), vals)
+
+ def test_to_bytes_val(self):
+ with self.subTest('fixed values in absence of user-supplied values'):
+ vals = { 'f3' : 0x00 } # | { 'f4' : 2, 'f1' : 0 }
+ self.assertEqual(self.S8V.to_bytes(vals), b'\x20')
+
+ with self.subTest('fixed values take precedence'):
+ vals = { 'f4' : 1, 'f1' : 1, 'f3' : 0 }
+ self.assertEqual(self.S8V.to_bytes(vals), b'\x20')
+
+ def test_from_bytes_val(self):
+ with self.assertRaises(codec.DecodeError):
+ self.S8V.from_bytes({ }, b'\xf0') # 'f4': 15 vs 2
+
+ with self.assertRaises(codec.DecodeError):
+ self.S8V.from_bytes({ }, b'\x08') # 'f1': 1 vs 0
+
+ # Field 'f3' takes any value, no exceptions shall be raised
+ for i in range(8):
+ data, vals = bytes([0x20 + i]), { 'f4' : 2, 'f1' : 0, 'f3' : i }
+ self.assertEqual(self.from_bytes(self.S8V, data), vals)
+
+ def test_to_bytes_spare(self):
+ self.assertEqual(self.S8P.to_bytes({ 'f4' : 0x00 }), b'\x00')
+ self.assertEqual(self.S8P.to_bytes({ 'f4' : 0x0f }), b'\x0f')
+ self.assertEqual(self.S8P.to_bytes({ 'f4' : 0xff }), b'\x0f')
+
+ def test_from_bytes_spare(self):
+ self.assertEqual(self.from_bytes(self.S8P, b'\x00'), { 'f4' : 0x00 })
+ self.assertEqual(self.from_bytes(self.S8P, b'\x0f'), { 'f4' : 0x0f })
+ self.assertEqual(self.from_bytes(self.S8P, b'\xff'), { 'f4' : 0x0f })
+
+class TestPDU(codec.Envelope):
+ STRUCT = (
+ codec.BitFieldSet(len=2, set=(
+ codec.BitField('ver', bl=4),
+ codec.BitField('flag', bl=1),
+ )),
+ codec.Uint16BE('len'),
+ codec.Buf('data'),
+ codec.Buf('tail', len=2),
+ )
+
+ def __init__(self, *args, **kw):
+ codec.Envelope.__init__(self, *args, **kw)
+ self.STRUCT[-3].get_val = lambda v: len(v['data'])
+ self.STRUCT[-2].get_len = lambda v, _: v['len']
+ self.STRUCT[-1].get_pres = lambda v: bool(v['flag'])
+
+ def check(self, vals: dict) -> None:
+ if not vals['ver'] in (0, 1, 2):
+ raise ValueError('Unknown version %d' % vals['ver'])
+
+class Envelope(unittest.TestCase):
+ def test_rest_octets(self):
+ pdu = TestPDU(check_len=False)
+ pdu.from_bytes(b'\x00' * 64)
+
+ with self.assertRaises(codec.DecodeError):
+ pdu = TestPDU(check_len=True)
+ pdu.from_bytes(b'\x00' * 64) # 'len' : 0
+
+ def test_field_raises(self):
+ pdu = TestPDU()
+ with self.assertRaises(codec.EncodeError):
+ pdu.c = { 'ver' : 0, 'flag' : 1, 'data' : b'\xff' * 16 }
+ pdu.to_bytes() # KeyError: 'tail' not found
+
+ def test_to_bytes(self):
+ pdu = TestPDU()
+
+ # No content in the new instances
+ self.assertEqual(pdu.c, { })
+
+ pdu.c = { 'ver' : 0, 'flag' : 1, 'data' : b'', 'tail' : b'\xde\xbe' }
+ self.assertEqual(pdu.to_bytes(), b'\x08\x00\x00\x00' + b'\xde\xbe')
+
+ pdu.c = { 'ver' : 1, 'flag' : 0, 'data' : b'\xff' * 15 }
+ self.assertEqual(pdu.to_bytes(), b'\x10\x00\x00\x0f' + b'\xff' * 15)
+
+ pdu.c = { 'ver' : 2, 'flag' : 1, 'data' : b'\xf0', 'tail' : b'\xbe\xed' }
+ self.assertEqual(pdu.to_bytes(), b'\x28\x00\x00\x01\xf0\xbe\xed')
+
+ def test_from_bytes(self):
+ pdu = TestPDU()
+
+ # No content in the new instances
+ self.assertEqual(pdu.c, { })
+
+ c = { 'ver' : 0, 'flag' : 1, 'len' : 0, 'data' : b'', 'tail' : b'\xde\xbe' }
+ pdu.from_bytes(b'\x08\x00\x00\x00' + b'\xde\xbe')
+ self.assertEqual(pdu.c, c)
+
+ c = { 'ver' : 1, 'flag' : 0, 'len' : 15, 'data' : b'\xff' * 15 }
+ pdu.from_bytes(b'\x10\x00\x00\x0f' + b'\xff' * 15)
+ self.assertEqual(pdu.c, c)
+
+ c = { 'ver' : 2, 'flag' : 1, 'len' : 1, 'data' : b'\xf0', 'tail' : b'\xbe\xed' }
+ pdu.from_bytes(b'\x28\x00\x00\x01\xf0\xbe\xed')
+ self.assertEqual(pdu.c, c)
+
+ def test_to_bytes_check(self):
+ pdu = TestPDU()
+
+ pdu.c = { 'ver' : 8, 'flag' : 1, 'data' : b'', 'tail' : b'\xde\xbe' }
+ with self.assertRaises(ValueError):
+ pdu.to_bytes()
+
+ def test_from_bytes_check(self):
+ pdu = TestPDU()
+
+ with self.assertRaises(ValueError):
+ pdu.from_bytes(b'\xf0\x00\x00\x00')
+
+class Sequence(unittest.TestCase):
+ class TLV(codec.Envelope):
+ STRUCT = (
+ codec.Uint('T'),
+ codec.Uint('L'),
+ codec.Buf('V'),
+ )
+
+ def __init__(self, *args, **kw) -> None:
+ codec.Envelope.__init__(self, *args, **kw)
+ self.STRUCT[-2].get_val = lambda v: len(v['V'])
+ self.STRUCT[-1].get_len = lambda v, _: v['L']
+
+ # Sequence of TLVs
+ SEQ = codec.Sequence(item=TLV())
+
+ Vseq, Bseq = [
+ { 'T' : 0xde, 'L' : 4, 'V' : b'\xde\xad\xbe\xef' },
+ { 'T' : 0xbe, 'L' : 2, 'V' : b'\xbe\xef' },
+ { 'T' : 0xbe, 'L' : 2, 'V' : b'\xef\xbe' },
+ { 'T' : 0x00, 'L' : 0, 'V' : b'' },
+ ], b''.join([
+ b'\xde\x04\xde\xad\xbe\xef',
+ b'\xbe\x02\xbe\xef',
+ b'\xbe\x02\xef\xbe',
+ b'\x00\x00',
+ ])
+
+ def test_to_bytes(self):
+ res = self.SEQ.to_bytes(self.Vseq)
+ self.assertEqual(res, self.Bseq)
+
+ def test_from_bytes(self):
+ res = self.SEQ.from_bytes(self.Bseq)
+ self.assertEqual(res, self.Vseq)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/target/trx_toolkit/test_data_dump.py b/src/target/trx_toolkit/test_data_dump.py
new file mode 100644
index 00000000..67ff32b8
--- /dev/null
+++ b/src/target/trx_toolkit/test_data_dump.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# TRX Toolkit
+# Unit tests for DATA capture management
+#
+# (C) 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.
+
+import unittest
+import tempfile
+import random
+
+from gsm_shared import *
+from data_dump import *
+
+class DATADump_Test(unittest.TestCase):
+ def setUp(self):
+ # Create a temporary file
+ self._tf = tempfile.TemporaryFile(mode = 'w+b')
+
+ # Create an instance of DATA dump manager
+ self._ddf = DATADumpFile(self._tf)
+
+ # Compare message a with message b
+ def _compare_msg(self, a, b):
+ # Make sure we're comparing messages of the same type
+ self.assertEqual(a.__class__, b.__class__)
+
+ # Compare common header fields
+ self.assertEqual(a.ver, b.ver)
+ self.assertEqual(a.fn, b.fn)
+ self.assertEqual(a.tn, b.tn)
+
+ # Burst bits (if present)
+ self.assertEqual(a.burst, b.burst)
+
+ # TxMsg specific fields
+ if isinstance(a, TxMsg):
+ self.assertEqual(a.pwr, b.pwr)
+
+ # RxMsg specific fields
+ if isinstance(a, RxMsg):
+ # Version independent fields
+ self.assertEqual(a.toa256, b.toa256)
+ self.assertEqual(a.rssi, b.rssi)
+
+ # Version specific fields
+ if a.ver >= 1:
+ self.assertEqual(a.nope_ind, b.nope_ind)
+ self.assertEqual(a.mod_type, b.mod_type)
+ self.assertEqual(a.tsc_set, b.tsc_set)
+ self.assertEqual(a.tsc, b.tsc)
+ self.assertEqual(a.ci, b.ci)
+
+ # Generate a random message of a given type / version
+ def _gen_rand_message(self, cls, ver = 1):
+ msg = cls(ver = ver)
+ msg.rand_hdr()
+ msg.rand_burst()
+ return msg
+
+ # Generate a list of random messages
+ def _gen_rand_messages(self, cls, count, ver = 1):
+ msg_list = []
+
+ for _ in range(count):
+ msg = self._gen_rand_message(cls, ver)
+ msg_list.append(msg)
+
+ return msg_list
+
+ # Generate a mixed list of random messages
+ def _gen_rand_message_mix(self, count, ver = 1):
+ msg_list = []
+ msg_list += self._gen_rand_messages(RxMsg, count)
+ msg_list += self._gen_rand_messages(TxMsg, count)
+ random.shuffle(msg_list)
+ return msg_list
+
+ def _test_store_and_parse(self, cls):
+ msg_ref = self._gen_rand_message(cls)
+ self._ddf.append_msg(msg_ref)
+
+ msg = self._ddf.parse_msg(0)
+ self._compare_msg(msg, msg_ref)
+
+ # Store one Rx message in a file, read it back and compare
+ def test_store_and_parse_rx_msg(self):
+ self._test_store_and_parse(RxMsg)
+
+ # Store one Tx message in a file, read it back and compare
+ def test_store_and_parse_tx_msg(self):
+ self._test_store_and_parse(TxMsg)
+
+ # Store multiple Rx/Tx messages in a file, read them back and compare
+ def test_store_and_parse_all(self):
+ # Store a mixed list of random messages (19 + 19)
+ msg_list_ref = self._gen_rand_message_mix(19)
+ self._ddf.append_all(msg_list_ref)
+
+ # Retrieve and compare stored messages
+ msg_list = self._ddf.parse_all()
+ for i in range(len(msg_list_ref)):
+ self._compare_msg(msg_list[i], msg_list_ref[i])
+
+ # Verify random access to stored messages
+ def test_parse_msg_idx(self):
+ # Store a mixed list of random messages (19 + 19)
+ msg_list_ref = self._gen_rand_message_mix(19)
+ self._ddf.append_all(msg_list_ref)
+
+ # Random access
+ for _ in range(100):
+ idx = random.randrange(len(msg_list_ref))
+ msg = self._ddf.parse_msg(idx)
+ self._compare_msg(msg, msg_list_ref[idx])
+
+ def test_parse_empty(self):
+ with self.assertLogs(level = 'ERROR'):
+ for idx in range(100):
+ msg = self._ddf.parse_msg(idx)
+ self.assertEqual(msg, None)
+
+ def test_parse_all_empty(self):
+ msg_list = self._ddf.parse_all()
+ self.assertEqual(msg_list, [])
+
+ def test_parse_len_overflow(self):
+ # Write a malformed message directly
+ self._tf.write(DATADump.TAG_TxMsg)
+ self._tf.write(b'\x00\x63') # 99
+ self._tf.write(b'\xff' * 90)
+
+ with self.assertLogs(level = 'ERROR'):
+ msg = self._ddf.parse_msg(0)
+ self.assertEqual(msg, None)
+
+ def test_parse_unknown_tag(self):
+ # Write a malformed message directly
+ self._tf.write(b'\x33')
+ self._tf.write(b'\x00\x63') # 99
+ self._tf.write(b'\xff' * 90)
+
+ with self.assertLogs(level = 'ERROR'):
+ msg = self._ddf.parse_msg(0)
+ self.assertEqual(msg, None)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/target/trx_toolkit/test_data_msg.py b/src/target/trx_toolkit/test_data_msg.py
new file mode 100644
index 00000000..ec6c2ec1
--- /dev/null
+++ b/src/target/trx_toolkit/test_data_msg.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# TRX Toolkit
+# Unit test for TRXD message codec
+#
+# (C) 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.
+
+import unittest
+
+from data_msg import Msg, TxMsg, RxMsg
+
+class Msg_Test(unittest.TestCase):
+ # Compare message a with message b
+ def _compare_msg(self, a, b):
+ # Make sure we're comparing messages of the same type
+ self.assertEqual(a.__class__, b.__class__)
+
+ # Compare common header fields
+ self.assertEqual(a.ver, b.ver)
+ self.assertEqual(a.fn, b.fn)
+ self.assertEqual(a.tn, b.tn)
+
+ # Burst bits (if present)
+ self.assertEqual(a.burst, b.burst)
+
+ # TxMsg specific fields
+ if isinstance(a, TxMsg):
+ self.assertEqual(a.pwr, b.pwr)
+
+ # RxMsg specific fields
+ if isinstance(a, RxMsg):
+ # Version independent fields
+ self.assertEqual(a.toa256, b.toa256)
+ self.assertEqual(a.rssi, b.rssi)
+
+ # Version specific fields
+ if a.ver >= 1:
+ self.assertEqual(a.nope_ind, b.nope_ind)
+ self.assertEqual(a.mod_type, b.mod_type)
+ self.assertEqual(a.tsc_set, b.tsc_set)
+ self.assertEqual(a.tsc, b.tsc)
+ self.assertEqual(a.ci, b.ci)
+
+ # Make sure that message validation throws a ValueError
+ def test_validate(self):
+ # Unknown version
+ with self.assertRaises(ValueError):
+ msg = RxMsg(fn = 0, tn = 0, ver = 100)
+ msg.validate()
+
+ # Uninitialized field
+ with self.assertRaises(ValueError):
+ msg = RxMsg()
+ msg.validate()
+ with self.assertRaises(ValueError):
+ msg = RxMsg(fn = None, tn = 0)
+ msg.validate()
+
+ # Out-of-range value(s)
+ with self.assertRaises(ValueError):
+ msg = RxMsg(fn = -1, tn = 0)
+ msg.validate()
+ with self.assertRaises(ValueError):
+ msg = RxMsg(fn = 0, tn = 10)
+ msg.validate()
+
+ # Validate header and burst randomization
+ def test_rand_hdr_burst(self):
+ tx_msg = TxMsg()
+ rx_msg = RxMsg()
+
+ for i in range(100):
+ tx_msg.rand_burst()
+ rx_msg.rand_burst()
+ tx_msg.rand_hdr()
+ rx_msg.rand_hdr()
+
+ tx_msg.validate()
+ rx_msg.validate()
+
+ def _test_enc_dec(self, msg, legacy = False, nope_ind = False):
+ # Prepare a given message (randomize)
+ msg.rand_hdr()
+
+ # NOPE.ind contains no burst
+ if not nope_ind:
+ msg.rand_burst()
+ else:
+ msg.nope_ind = True
+ msg.mod_type = None
+ msg.tsc_set = None
+ msg.tsc = None
+
+ # Encode a given message to bytes
+ msg_enc = msg.gen_msg(legacy)
+
+ # Decode a new message from bytes
+ msg_dec = msg.__class__()
+ msg_dec.parse_msg(msg_enc)
+
+ # Compare decoded vs the original
+ self._compare_msg(msg, msg_dec)
+
+ # Validate encoding and decoding
+ def test_enc_dec(self):
+ for ver in Msg.KNOWN_VERSIONS:
+ with self.subTest("TxMsg", ver = ver):
+ msg = TxMsg(ver = ver)
+ self._test_enc_dec(msg)
+
+ with self.subTest("RxMsg", ver = ver):
+ msg = RxMsg(ver = ver)
+ self._test_enc_dec(msg)
+
+ if ver >= 1:
+ with self.subTest("RxMsg NOPE.ind", ver = ver):
+ msg = RxMsg(ver = ver)
+ self._test_enc_dec(msg, nope_ind = True)
+
+ with self.subTest("RxMsg (legacy transceiver)"):
+ msg = RxMsg(ver = 0)
+ self._test_enc_dec(msg, legacy = True)
+
+ # Validate bit conversations
+ def test_bit_conv(self):
+ usbits_ref = list(range(0, 256))
+ sbits_ref = list(range(-127, 128))
+
+ # Test both usbit2sbit() and sbit2usbit()
+ sbits = Msg.usbit2sbit(usbits_ref)
+ usbits = Msg.sbit2usbit(sbits)
+ self.assertEqual(usbits[:255], usbits_ref[:255])
+ self.assertEqual(usbits[255], 254)
+
+ # Test both sbit2ubit() and ubit2sbit()
+ ubits = Msg.sbit2ubit(sbits_ref)
+ self.assertEqual(ubits, ([1] * 127 + [0] * 128))
+
+ sbits = Msg.ubit2sbit(ubits)
+ self.assertEqual(sbits, ([-127] * 127 + [127] * 128))
+
+ def _test_transform(self, msg):
+ # Prepare given messages
+ msg.rand_hdr()
+ msg.rand_burst()
+
+ # Perform message transformation
+ if isinstance(msg, TxMsg):
+ msg_trans = msg.trans()
+ else:
+ msg_trans = msg.trans()
+
+ self.assertEqual(msg_trans.ver, msg.ver)
+ self.assertEqual(msg_trans.fn, msg.fn)
+ self.assertEqual(msg_trans.tn, msg.tn)
+
+ if isinstance(msg, RxMsg):
+ burst = Msg.sbit2ubit(msg.burst)
+ self.assertEqual(msg_trans.burst, burst)
+ else:
+ burst = Msg.ubit2sbit(msg.burst)
+ self.assertEqual(msg_trans.burst, burst)
+
+ # Validate message transformation
+ def test_transform(self):
+ for ver in Msg.KNOWN_VERSIONS:
+ with self.subTest("TxMsg", ver = ver):
+ msg = TxMsg(ver = ver)
+ self._test_transform(msg)
+
+ with self.subTest("RxMsg", ver = ver):
+ msg = RxMsg(ver = ver)
+ self._test_transform(msg)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py
index 37680e75..ffd18abd 100644
--- a/src/target/trx_toolkit/transceiver.py
+++ b/src/target/trx_toolkit/transceiver.py
@@ -1,10 +1,10 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
# Transceiver implementation
#
-# (C) 2018-2019 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
#
# All Rights Reserved
#
@@ -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
@@ -29,6 +25,8 @@ from data_if import DATAInterface
from udp_link import UDPLink
from trx_list import TRXList
+from gsm_shared import HoppingParams
+
class Transceiver:
""" Base transceiver implementation.
@@ -45,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
@@ -67,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)
@@ -88,54 +77,86 @@ class Transceiver:
that shall provide at least one method: measure(freq). This
is required for the MS side (i.e. OsmocomBB).
+ == Frequency hopping (optional)
+
+ There are two ways to implement frequency hopping:
+
+ a) The Transceiver is configured with the hopping parameters, in
+ particular HSN, MAIO, and the list of ARFCNs (channels), so the
+ actual Rx/Tx frequencies are changed by the Transceiver itself
+ depending on the current TDMA frame number.
+
+ b) The L1 maintains several Transceivers (two or more), so each
+ instance is assigned one dedicated RF carrier frequency, and
+ hence the number of available hopping frequencies is equal to
+ the number of Transceivers. In this case, it's the task of
+ the L1 to commutate bursts between Transceivers (frequencies).
+
+ Variant a) is commonly known as "synthesizer frequency hopping"
+ whereas b) is known as "baseband frequency hopping".
+
+ For the MS side, a) is preferred, because a phone usually has only
+ one Transceiver (per RAT). On the other hand, b) is more suitable
+ for the BTS side, because it's relatively easy to implement and
+ there is no technical limitation on the amount of Transceivers.
+
+ FakeTRX obviously does support b) since multi-TRX feature has been
+ implemented, as well as a) by resolving UL/DL frequencies using a
+ preconfigured (by the L1) set of the hopping parameters. The later
+ can be enabled using the SETFH control command.
+
+ NOTE: in the current implementation, mode a) applies to the whole
+ Transceiver and all its timeslots, so using in for the BTS side
+ does not make any sense (imagine BCCH hopping together with DCCH).
+
"""
- 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
# Actual RX / TX frequencies
- self.rx_freq = None
- self.tx_freq = None
+ self._rx_freq = None
+ self._tx_freq = None
- # List of active (configured) timeslots
- self.ts_list = []
+ # Frequency hopping parameters (set by CTRL)
+ self.fh = None
# List of child transceivers
self.child_trx_list = TRXList()
@@ -149,18 +170,57 @@ class Transceiver:
return desc
+ @property
+ def ready(self):
+ # Make sure that either both Rx/Tx frequencies are set
+ if self._rx_freq is None or self._tx_freq is None:
+ # ... or frequency hopping is in use
+ if self.fh is None:
+ return False
+
+ return True
+
+ def get_rx_freq(self, fn):
+ if self.fh is None:
+ return self._rx_freq
+
+ # Frequency hopping in use, resolve by TDMA fn
+ (rx_freq, _) = self.fh.resolve(fn)
+ return rx_freq
+
+ def get_tx_freq(self, fn):
+ if self.fh is None:
+ return self._tx_freq
+
+ # Frequency hopping in use, resolve by TDMA fn
+ (_, tx_freq) = self.fh.resolve(fn)
+ return tx_freq
+
+ def enable_fh(self, *args):
+ self.fh = HoppingParams(*args)
+ log.info("(%s) Frequency hopping configured: %s" % (self, self.fh))
+
+ def disable_fh(self):
+ if self.fh is not None:
+ log.info("(%s) Frequency hopping disabled" % self)
+ self.fh = None
+
# To be overwritten if required,
# no custom command handlers by default
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
- else:
- 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()
# Trigger clock generator if required
if self.clck_gen is not None:
@@ -172,16 +232,16 @@ class Transceiver:
# Transceiver was started
clck_links.append(self.clck_if)
- if not self.clck_gen.timer and len(clck_links) > 0:
+ if not self.clck_gen.running and len(clck_links) > 0:
log.info("Starting clock generator")
self.clck_gen.start()
- elif self.clck_gen.timer and not clck_links:
+ elif self.clck_gen.running and not clck_links:
log.info("Stopping clock generator")
self.clck_gen.stop()
def recv_data_msg(self):
# Read and parse data from socket
- msg = self.data_if.recv_l12trx_msg()
+ msg = self.data_if.recv_tx_msg()
if not msg:
return None
@@ -191,10 +251,8 @@ class Transceiver:
"is not running => dropping..." % (self, msg.desc_hdr()))
return None
- # Make sure that indicated timeslot is configured
- if msg.tn not in self.ts_list:
- 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):
+ # TODO: make legacy mode configurable (via argv?)
+ self.data_if.send_msg(msg, legacy = True)
diff --git a/src/target/trx_toolkit/trx_list.py b/src/target/trx_toolkit/trx_list.py
index 8b4013dd..c476a7d0 100644
--- a/src/target/trx_toolkit/trx_list.py
+++ b/src/target/trx_toolkit/trx_list.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -17,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.
@@ -31,9 +26,10 @@ class TRXList:
"""
- def __init__(self):
- self.trx_list = []
+ def __init__(self, trx_list = None):
+ self.trx_list = trx_list or []
+ # NOTE: do not use this in performance critical parts
def __getitem__(self, i):
return self.trx_list[i]
diff --git a/src/target/trx_toolkit/trx_sniff.py b/src/target/trx_toolkit/trx_sniff.py
index 7e5c2bd6..c91e3e0b 100755
--- a/src/target/trx_toolkit/trx_sniff.py
+++ b/src/target/trx_toolkit/trx_sniff.py
@@ -4,7 +4,7 @@
# TRX Toolkit
# Scapy-based TRX interface sniffer
#
-# (C) 2018-2019 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
#
# All Rights Reserved
#
@@ -17,12 +17,8 @@
# 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-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
+APP_CR_HOLDERS = [("2018-2020", "Vadim Yanitskiy <axilirator@gmail.com>")]
import logging as log
import argparse
@@ -101,10 +97,10 @@ class Application(ApplicationBase):
msg_raw = bytearray(trx.load)
# Determine a burst direction (L1 <-> TRX)
- l12trx = udp.sport > udp.dport
+ tx_dir = udp.sport > udp.dport
# Create an empty DATA message
- msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1()
+ msg = TxMsg() if tx_dir else RxMsg()
# Attempt to parse the payload as a DATA message
try:
@@ -112,21 +108,19 @@ class Application(ApplicationBase):
msg.validate()
except ValueError as e:
desc = msg.desc_hdr()
- if desc is "":
+ if desc == "":
desc = "parsing error"
log.warning("Ignoring an incorrect message (%s): %s" % (desc, e))
self.cnt_burst_dropped_num += 1
return
# Poke burst pass filter
- rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn)
- if rc is False:
+ if not self.burst_pass_filter(msg):
self.cnt_burst_dropped_num += 1
return
# Debug print
- log.debug("%s burst: %s" \
- % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))
+ log.debug("%s burst: %s", "L1 -> TRX" if tx_dir else "TRX -> L1", msg.desc_hdr())
# Poke message handler
self.msg_handle(msg)
@@ -136,27 +130,43 @@ class Application(ApplicationBase):
if rc is True:
self.shutdown()
- def burst_pass_filter(self, l12trx, fn, tn):
+ def burst_pass_filter(self, msg):
# Direction filter
if self.argv.direction is not None:
- if self.argv.direction == "TRX" and not l12trx:
- return False
- elif self.argv.direction == "L1" and l12trx:
- return False
+ if self.argv.direction == "TRX": # L1 -> TRX
+ if not isinstance(msg, TxMsg):
+ return False
+ elif self.argv.direction == "L1": # TRX -> L1
+ if not isinstance(msg, RxMsg):
+ return False
# Timeslot filter
if self.argv.pf_tn is not None:
- if tn != self.argv.pf_tn:
+ if msg.tn != self.argv.pf_tn:
return False
# Frame number filter
if self.argv.pf_fn_lt is not None:
- if fn > self.argv.pf_fn_lt:
+ if msg.fn > self.argv.pf_fn_lt:
return False
if self.argv.pf_fn_gt is not None:
- if fn < self.argv.pf_fn_gt:
+ if msg.fn < self.argv.pf_fn_gt:
return False
+ # Message type specific filtering
+ if isinstance(msg, RxMsg):
+ # NOPE.ind filter
+ if not self.argv.pf_nope_ind and msg.nope_ind:
+ return False
+
+ # RSSI filter
+ if self.argv.pf_rssi_min is not None:
+ if msg.rssi < self.argv.pf_rssi_min:
+ return False
+ if self.argv.pf_rssi_max is not None:
+ if msg.rssi > self.argv.pf_rssi_max:
+ return False
+
# Burst passed ;)
return True
@@ -257,6 +267,15 @@ class Application(ApplicationBase):
pf_group.add_argument("--frame-num-gt", metavar = "FN",
dest = "pf_fn_gt", type = int,
help = "TDMA frame number (greater than FN)")
+ pf_group.add_argument("--no-nope-ind",
+ dest = "pf_nope_ind", action = "store_false",
+ help = "Ignore NOPE.ind (NOPE / IDLE indications)")
+ pf_group.add_argument("--rssi-min", metavar = "RSSI",
+ dest = "pf_rssi_min", type = int,
+ help = "Minimum RSSI value (e.g. -75)")
+ pf_group.add_argument("--rssi-max", metavar = "RSSI",
+ dest = "pf_rssi_max", type = int,
+ help = "Maximum RSSI value (e.g. -50)")
return parser.parse_args()
diff --git a/src/target/trx_toolkit/trxd_proto.py b/src/target/trx_toolkit/trxd_proto.py
new file mode 100644
index 00000000..a1bf1b53
--- /dev/null
+++ b/src/target/trx_toolkit/trxd_proto.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+
+'''
+TRXD PDU definitions based on declarative codec.
+'''
+
+# TRX Toolkit
+#
+# (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+# Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+#
+# All Rights Reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU 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.
+
+import codec
+
+class Header(codec.BitFieldSet):
+ ''' Header constructor for TRXD PDUs. '''
+
+ def __init__(self, ver: int, batched: bool = False):
+ f = [ # Dynamically generated field list
+ codec.BitField('ver', bl=4, val=ver) if not batched
+ else codec.BitField.Spare(bl=4), # RFU
+ codec.BitField.Spare(bl=1),
+ codec.BitField('tn', bl=3),
+ ]
+
+ if ver >= 2: # TRXDv2 and higher
+ f.append(codec.BitField('batch', bl=1))
+ f.append(codec.BitField('shadow', bl=1) if batched
+ else codec.BitField.Spare(bl=1))
+ f.append(codec.BitField('trxn', bl=6))
+
+ codec.BitFieldSet.__init__(self, set=tuple(f))
+
+class MTS(codec.BitFieldSet):
+ ''' Modulation and Training Sequence. '''
+
+ DEF_LEN = 1
+ STRUCT = (
+ codec.BitField('nope', bl=1),
+ codec.BitField('mod', bl=4),
+ codec.BitField('tsc', bl=3),
+ )
+
+ @staticmethod
+ def get_burst_len(mod: int) -> int:
+ ''' Get burst length by modulation type. '''
+
+ GMSK_BURST_LEN = 148
+
+ if (mod >> 2) == 0b00: # GMSK
+ return 1 * GMSK_BURST_LEN
+ elif (mod >> 2) == 0b11: # AQPSK
+ return 2 * GMSK_BURST_LEN
+ elif (mod >> 1) == 0b010: # 8-PSK
+ return 3 * GMSK_BURST_LEN
+ elif (mod >> 1) == 0b100: # 16QAM
+ return 4 * GMSK_BURST_LEN
+ elif (mod >> 1) == 0b101: # 32QAM
+ return 5 * GMSK_BURST_LEN
+ elif mod == 0b0110: # GMSK (Access Burst)
+ return 1 * GMSK_BURST_LEN
+ raise ValueError('Unknown modulation type')
+
+class BurstBits(codec.Buf):
+ ''' Soft-/hard-bits with variable length. '''
+
+ def __init__(self, name: str, *args, **kw):
+ codec.Buf.__init__(self, name, *args, **kw)
+
+ # This field has a variable length that depends on moduletion type
+ self.get_len = lambda v, _: MTS.get_burst_len(v['mod'])
+ # This field is not present in NOPE / IDLE indications
+ self.get_pres = lambda v: not v['nope']
+
+
+class PDUv0Rx(codec.Envelope):
+ STRUCT = (
+ Header(ver=0),
+ codec.Uint32BE('fn'),
+ codec.Uint('rssi', mult=-1),
+ codec.Int16BE('toa256'),
+ codec.Buf('soft-bits'),
+ codec.Buf('pad'), # Optional
+ )
+
+ def __init__(self, *args, **kw):
+ codec.Envelope.__init__(self, *args, **kw)
+
+ # Field 'soft-bits' is either 148 (GMSK) or 444 (8-PSK) octets long
+ self.STRUCT[-2].get_len = lambda _, data: 444 if len(data) > 148 else 148
+
+class PDUv0Tx(codec.Envelope):
+ STRUCT = (
+ Header(ver=0),
+ codec.Uint32BE('fn'),
+ codec.Uint('pwr'),
+ codec.Buf('hard-bits'),
+ )
+
+
+class PDUv1Rx(codec.Envelope):
+ STRUCT = (
+ Header(ver=1),
+ codec.Uint32BE('fn'),
+ codec.Uint('rssi', mult=-1),
+ codec.Int16BE('toa256'),
+ MTS(),
+ codec.Int16BE('cir'),
+ BurstBits('soft-bits'),
+ )
+
+class PDUv1Tx(PDUv0Tx):
+ # Same structure as PDUv0Tx, only the version is different
+ STRUCT = (
+ Header(ver=1),
+ *PDUv0Tx.STRUCT[1:]
+ )
+
+
+class PDUv2Rx(codec.Envelope):
+ class BPDU(codec.Envelope):
+ ''' Batched PDU part. '''
+ STRUCT = (
+ Header(ver=2, batched=True),
+ MTS(),
+ codec.Uint('rssi', mult=-1),
+ codec.Int16BE('toa256'),
+ codec.Int16BE('cir'),
+ BurstBits('soft-bits'),
+ )
+
+ STRUCT = (
+ Header(ver=2),
+ MTS(),
+ codec.Uint('rssi', mult=-1),
+ codec.Int16BE('toa256'),
+ codec.Int16BE('cir'),
+ codec.Uint32BE('fn'),
+ BurstBits('soft-bits'),
+ codec.Sequence(item=BPDU()).f('bpdu'),
+ )
+
+class PDUv2Tx(codec.Envelope):
+ class BPDU(codec.Envelope):
+ ''' Batched PDU part. '''
+ STRUCT = (
+ Header(ver=2, batched=True),
+ MTS(),
+ codec.Uint('pwr'),
+ codec.Int('scpir'),
+ codec.Spare('spare', len=3),
+ BurstBits('hard-bits'),
+ )
+
+ STRUCT = (
+ Header(ver=2),
+ MTS(),
+ codec.Uint('pwr'),
+ codec.Int('scpir'),
+ codec.Spare('spare', len=3),
+ codec.Uint32BE('fn'),
+ BurstBits('hard-bits'),
+ codec.Sequence(item=BPDU()).f('bpdu'),
+ )
diff --git a/src/target/trx_toolkit/udp_link.py b/src/target/trx_toolkit/udp_link.py
index 8da15db9..c3f14765 100644
--- a/src/target/trx_toolkit/udp_link.py
+++ b/src/target/trx_toolkit/udp_link.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TRX Toolkit
@@ -17,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
@@ -29,7 +24,7 @@ class UDPLink:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((bind_addr, bind_port))
- self.sock.setblocking(0)
+ self.sock.setblocking(False)
# Save remote info
self.remote_addr = remote_addr
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
diff --git a/src/target_dsp/calypso/dump2coff.py b/src/target_dsp/calypso/dump2coff.py
index c05a0ffc..17ff9faf 100755
--- a/src/target_dsp/calypso/dump2coff.py
+++ b/src/target_dsp/calypso/dump2coff.py
@@ -104,7 +104,7 @@ class CalypsoCOFF(object):
sptr, # long int s_scnptr; /* File pointer to raw data */
0, # long int s_relptr; /* File pointer to relocation entries */
0, # long int s_lnnoptr;/* File pointer to line number entries */
- 0, # unsigned short s_nreloc; /* Number of relocation entrie */
+ 0, # unsigned short s_nreloc; /* Number of relocation entries */
0, # unsigned short s_nlnno; /* Number of line number entries */
s.flags,# unsigned short s_flags; /* Flags (see ``Section header flags'') */
'\x00', # /