summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rwxr-xr-xcontrib/jenkins.sh41
-rw-r--r--doc/examples/mobile/default.cfg2
-rw-r--r--doc/examples/mobile/multi_ms.cfg4
-rw-r--r--doc/manuals/Makefile8
-rw-r--r--doc/manuals/chapters/scripting.adoc111
-rwxr-xr-xdoc/manuals/osmo-gsm-manuals-dir.sh26
-rw-r--r--doc/manuals/osmocombb-usermanual-docinfo.xml57
-rw-r--r--doc/manuals/osmocombb-usermanual.adoc21
-rw-r--r--include/l1ctl_proto.h30
-rwxr-xr-xsrc/host/fb_tools/bdf_to_c.py66
-rw-r--r--src/host/layer23/README2
-rw-r--r--src/host/layer23/include/osmocom/bb/common/Makefile.am3
-rw-r--r--src/host/layer23/include/osmocom/bb/common/l1ctl.h2
-rw-r--r--src/host/layer23/include/osmocom/bb/common/osmocom_data.h12
-rw-r--r--src/host/layer23/include/osmocom/bb/common/sap_fsm.h35
-rw-r--r--src/host/layer23/include/osmocom/bb/common/sap_interface.h84
-rw-r--r--src/host/layer23/include/osmocom/bb/common/sap_proto.h121
-rw-r--r--src/host/layer23/include/osmocom/bb/common/sysinfo.h2
-rw-r--r--src/host/layer23/include/osmocom/bb/misc/Makefile.am2
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/Makefile.am3
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/app_mobile.h2
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm322.h10
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h13
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h5
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/mncc.h4
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/settings.h19
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/subscriber.h7
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/vty.h1
-rw-r--r--src/host/layer23/src/common/Makefile.am2
-rw-r--r--src/host/layer23/src/common/gps.c29
-rw-r--r--src/host/layer23/src/common/l1ctl.c123
-rw-r--r--src/host/layer23/src/common/l1l2_interface.c1
-rw-r--r--src/host/layer23/src/common/main.c6
-rw-r--r--src/host/layer23/src/common/networks.c12
-rw-r--r--src/host/layer23/src/common/sap_fsm.c691
-rw-r--r--src/host/layer23/src/common/sap_interface.c642
-rw-r--r--src/host/layer23/src/common/sap_proto.c322
-rw-r--r--src/host/layer23/src/common/sim.c19
-rw-r--r--src/host/layer23/src/common/sysinfo.c2
-rw-r--r--src/host/layer23/src/misc/Makefile.am5
-rw-r--r--src/host/layer23/src/misc/app_bcch_scan.c5
-rw-r--r--src/host/layer23/src/misc/app_cbch_sniff.c17
-rw-r--r--src/host/layer23/src/misc/app_ccch_scan.c2
-rw-r--r--src/host/layer23/src/misc/bcch_scan.c10
-rw-r--r--src/host/layer23/src/misc/bcch_scan.h6
-rw-r--r--src/host/layer23/src/misc/cell_log.c2
-rw-r--r--src/host/layer23/src/misc/geo.c47
-rw-r--r--src/host/layer23/src/misc/geo.h12
-rw-r--r--src/host/layer23/src/mobile/Makefile.am2
-rw-r--r--src/host/layer23/src/mobile/app_mobile.c47
-rw-r--r--src/host/layer23/src/mobile/gsm322.c102
-rw-r--r--src/host/layer23/src/mobile/gsm411_sms.c9
-rw-r--r--src/host/layer23/src/mobile/gsm414.c220
-rw-r--r--src/host/layer23/src/mobile/gsm480_ss.c23
-rw-r--r--src/host/layer23/src/mobile/gsm48_cc.c2
-rw-r--r--src/host/layer23/src/mobile/gsm48_mm.c47
-rw-r--r--src/host/layer23/src/mobile/gsm48_rr.c131
-rw-r--r--src/host/layer23/src/mobile/main.c55
-rw-r--r--src/host/layer23/src/mobile/mncc_sock.c24
-rw-r--r--src/host/layer23/src/mobile/mnccms.c6
-rw-r--r--src/host/layer23/src/mobile/script_lua.c10
-rw-r--r--src/host/layer23/src/mobile/settings.c10
-rw-r--r--src/host/layer23/src/mobile/subscriber.c68
-rw-r--r--src/host/layer23/src/mobile/voice.c52
-rw-r--r--src/host/layer23/src/mobile/vty_interface.c113
-rw-r--r--src/host/osmocon/osmocon.c62
-rw-r--r--src/host/osmocon/tpu_debug.c62
-rw-r--r--src/host/trxcon/l1ctl.c282
-rw-r--r--src/host/trxcon/l1ctl.h3
-rw-r--r--src/host/trxcon/l1ctl_link.c64
-rw-r--r--src/host/trxcon/l1ctl_link.h4
-rw-r--r--src/host/trxcon/logging.c5
-rw-r--r--src/host/trxcon/logging.h2
-rw-r--r--src/host/trxcon/sched_clck.c29
-rw-r--r--src/host/trxcon/sched_lchan_common.c68
-rw-r--r--src/host/trxcon/sched_lchan_desc.c809
-rw-r--r--src/host/trxcon/sched_lchan_pdtch.c51
-rw-r--r--src/host/trxcon/sched_lchan_rach.c152
-rw-r--r--src/host/trxcon/sched_lchan_sch.c7
-rw-r--r--src/host/trxcon/sched_lchan_tchf.c64
-rw-r--r--src/host/trxcon/sched_lchan_tchh.c67
-rw-r--r--src/host/trxcon/sched_lchan_xcch.c56
-rw-r--r--src/host/trxcon/sched_mframe.c109
-rw-r--r--src/host/trxcon/sched_prim.c40
-rw-r--r--src/host/trxcon/sched_trx.c279
-rw-r--r--src/host/trxcon/sched_trx.h89
-rw-r--r--src/host/trxcon/scheduler.h22
-rw-r--r--src/host/trxcon/trx_if.c198
-rw-r--r--src/host/trxcon/trx_if.h14
-rw-r--r--src/host/trxcon/trxcon.c86
-rw-r--r--src/host/trxcon/trxcon.h2
-rw-r--r--src/host/virt_phy/configure.ac13
-rw-r--r--src/host/virt_phy/include/virtphy/l1ctl_sap.h1
-rw-r--r--src/host/virt_phy/include/virtphy/virt_l1_model.h15
-rw-r--r--src/host/virt_phy/include/virtphy/virtual_um.h4
-rw-r--r--src/host/virt_phy/src/gsmtapl1_if.c103
-rw-r--r--src/host/virt_phy/src/l1ctl_sap.c14
-rw-r--r--src/host/virt_phy/src/l1ctl_sock.c11
-rw-r--r--src/host/virt_phy/src/logging.c15
-rw-r--r--src/host/virt_phy/src/shared/osmo_mcast_sock.c4
-rw-r--r--src/host/virt_phy/src/shared/virtual_um.c76
-rw-r--r--src/host/virt_phy/src/virt_l1_sched_simple.c9
-rw-r--r--src/host/virt_phy/src/virt_prim_data.c9
-rw-r--r--src/host/virt_phy/src/virt_prim_pm.c39
-rw-r--r--src/host/virt_phy/src/virt_prim_traffic.c19
-rw-r--r--src/host/virt_phy/src/virtphy.c20
-rw-r--r--src/target/firmware/Makefile45
-rw-r--r--src/target/firmware/Makefile.inc6
-rw-r--r--src/target/firmware/abb/twl3025.c37
-rw-r--r--src/target/firmware/apps/layer1/main.c2
-rw-r--r--src/target/firmware/apps/menu/main.c337
-rw-r--r--src/target/firmware/apps/rssi/main.c6
-rw-r--r--src/target/firmware/apps/snake_game/main.c521
-rw-r--r--src/target/firmware/board/common/readcal_tiffs.c79
-rw-r--r--src/target/firmware/board/common/tx_calchan.c210
-rw-r--r--src/target/firmware/board/compal/highram.lds2
-rw-r--r--src/target/firmware/board/compal/keymap.h3
-rw-r--r--src/target/firmware/board/compal/ram.lds6
-rw-r--r--src/target/firmware/board/compal/readcal_common.c124
-rw-r--r--src/target/firmware/board/compal/readcal_small.c30
-rw-r--r--src/target/firmware/board/compal/rf_power.c62
-rw-r--r--src/target/firmware/board/compal/rf_tables.c194
-rw-r--r--src/target/firmware/board/compal/rffe_dualband.c24
-rw-r--r--src/target/firmware/board/compal_e86/init.c4
-rw-r--r--src/target/firmware/board/compal_e86/rffe_dualband_e86.c24
-rw-r--r--src/target/firmware/board/compal_e86/tx_ramps.c435
-rw-r--r--src/target/firmware/board/compal_e88/flash.lds4
-rwxr-xr-xsrc/target/firmware/board/compal_e88/init.c4
-rw-r--r--src/target/firmware/board/compal_e88/tx_ramps.c436
-rw-r--r--src/target/firmware/board/compal_e99/init.c6
-rw-r--r--src/target/firmware/board/compal_e99/readcal.c30
-rw-r--r--src/target/firmware/board/fcdev3b/init.c145
-rw-r--r--src/target/firmware/board/gta0x/afcparams.c53
-rw-r--r--src/target/firmware/board/gta0x/init.c27
-rw-r--r--src/target/firmware/board/gta0x/rf_power.c63
-rw-r--r--src/target/firmware/board/gta0x/rf_tables.c573
-rw-r--r--src/target/firmware/board/gta0x/rffe_gta0x_triband.c20
-rw-r--r--src/target/firmware/board/gtm900b/afcparams.c47
-rw-r--r--src/target/firmware/board/gtm900b/init.c221
-rw-r--r--src/target/firmware/board/gtm900b/keymap.h29
-rw-r--r--src/target/firmware/board/gtm900b/rffe_gtm900b.c197
-rw-r--r--src/target/firmware/board/mediatek/ram.lds2
-rw-r--r--src/target/firmware/board/pirelli_dpl10/init.c8
-rw-r--r--src/target/firmware/board/pirelli_dpl10/keymap.h5
-rw-r--r--src/target/firmware/board/pirelli_dpl10/readcal.c91
-rw-r--r--src/target/firmware/board/pirelli_dpl10/rf_power.c63
-rw-r--r--src/target/firmware/board/pirelli_dpl10/rf_tables.c597
-rw-r--r--src/target/firmware/board/se_j100/tx_ramps.c441
-rw-r--r--src/target/firmware/calypso/keypad.c23
-rw-r--r--src/target/firmware/calypso/sim.c2
-rw-r--r--src/target/firmware/calypso/tpu.c70
-rw-r--r--src/target/firmware/comm/msgb.c2
-rw-r--r--src/target/firmware/comm/sercomm.c2
-rw-r--r--src/target/firmware/fb/fb_bw8.c63
-rw-r--r--src/target/firmware/fb/fb_rgb332.c2
-rw-r--r--src/target/firmware/fb/fb_s6b33b1x.c2
-rw-r--r--src/target/firmware/fb/fb_st7558.c1
-rw-r--r--src/target/firmware/flash/cfi_flash.c40
-rwxr-xr-xsrc/target/firmware/include/abb/twl3025.h5
-rw-r--r--src/target/firmware/include/calypso/dsp_api.h12
-rw-r--r--src/target/firmware/include/calypso/l1_environment.h4
-rwxr-xr-xsrc/target/firmware/include/calypso/sim.h2
-rw-r--r--src/target/firmware/include/comm/timer.h2
-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/flash/cfi_flash.h10
-rw-r--r--src/target/firmware/include/keypad.h4
-rw-r--r--src/target/firmware/include/layer1/apc.h10
-rw-r--r--src/target/firmware/include/layer1/async.h6
-rw-r--r--src/target/firmware/include/layer1/mframe_sched.h4
-rw-r--r--src/target/firmware/include/layer1/rfch.h2
-rw-r--r--src/target/firmware/include/layer1/sync.h6
-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/include/string.h3
-rw-r--r--src/target/firmware/include/tiffs.h33
-rw-r--r--src/target/firmware/layer1/Makefile2
-rw-r--r--src/target/firmware/layer1/afc.c11
-rw-r--r--src/target/firmware/layer1/apc.c57
-rw-r--r--src/target/firmware/layer1/l23_api.c57
-rw-r--r--src/target/firmware/layer1/mframe_sched.c57
-rw-r--r--src/target/firmware/layer1/prim_fbsb.c4
-rw-r--r--src/target/firmware/layer1/prim_freq.c5
-rw-r--r--src/target/firmware/layer1/prim_rx_nb.c2
-rw-r--r--src/target/firmware/layer1/prim_tch.c8
-rw-r--r--src/target/firmware/layer1/prim_utils.c18
-rw-r--r--src/target/firmware/layer1/rfch.c19
-rw-r--r--src/target/firmware/layer1/sync.c65
-rw-r--r--src/target/firmware/lib/Makefile5
-rw-r--r--src/target/firmware/lib/index.c12
-rw-r--r--src/target/firmware/lib/memcmp.S18
-rw-r--r--src/target/firmware/lib/strcmp.c12
-rw-r--r--src/target/firmware/lib/vsprintf.c2
-rw-r--r--src/target/firmware/rf/trf6151.c4
-rwxr-xr-xsrc/target/firmware/solve_envs.py4
-rw-r--r--src/target/firmware/tiffs/Makefile5
-rw-r--r--src/target/firmware/tiffs/globals.c12
-rw-r--r--src/target/firmware/tiffs/globals.h9
-rw-r--r--src/target/firmware/tiffs/init.c104
-rw-r--r--src/target/firmware/tiffs/readfile.c265
-rw-r--r--src/target/trx_toolkit/README10
-rw-r--r--src/target/trx_toolkit/app_common.py102
-rw-r--r--src/target/trx_toolkit/burst_fwd.py276
-rwxr-xr-xsrc/target/trx_toolkit/burst_gen.py285
-rwxr-xr-xsrc/target/trx_toolkit/burst_send.py221
-rwxr-xr-xsrc/target/trx_toolkit/clck_gen.py94
-rw-r--r--src/target/trx_toolkit/copyright.py13
-rwxr-xr-xsrc/target/trx_toolkit/ctrl_cmd.py111
-rw-r--r--src/target/trx_toolkit/ctrl_if.py26
-rw-r--r--src/target/trx_toolkit/ctrl_if_bb.py213
-rw-r--r--src/target/trx_toolkit/ctrl_if_bts.py187
-rw-r--r--src/target/trx_toolkit/ctrl_if_trx.py266
-rw-r--r--src/target/trx_toolkit/data_dump.py173
-rw-r--r--src/target/trx_toolkit/data_if.py95
-rw-r--r--src/target/trx_toolkit/data_msg.py860
-rw-r--r--src/target/trx_toolkit/fake_pm.py72
-rwxr-xr-xsrc/target/trx_toolkit/fake_trx.py646
-rw-r--r--src/target/trx_toolkit/gsm_shared.py153
-rw-r--r--src/target/trx_toolkit/rand_burst_gen.py90
-rw-r--r--src/target/trx_toolkit/test_data_dump.py165
-rw-r--r--src/target/trx_toolkit/test_data_msg.py193
-rw-r--r--src/target/trx_toolkit/transceiver.py281
-rw-r--r--src/target/trx_toolkit/trx_list.py63
-rwxr-xr-xsrc/target/trx_toolkit/trx_sniff.py289
-rw-r--r--src/target/trx_toolkit/udp_link.py3
-rwxr-xr-xsrc/target_dsp/calypso/bin2cfile.py4
-rwxr-xr-xsrc/target_dsp/calypso/dump2coff.py2
230 files changed, 13649 insertions, 4163 deletions
diff --git a/.gitignore b/.gitignore
index 1a01c26c..2232749c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,13 @@ core.*
/.project
/.settings/
+# manuals
+doc/manuals/*.html
+doc/manuals/*.svg
+doc/manuals/*.pdf
+doc/manuals/*__*.png
+doc/manuals/*.check
+doc/manuals/generated/
+doc/manuals/osmocombb-usermanual.xml
+doc/manuals/common
+doc/manuals/build
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index f886c218..fafbdfba 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -1,4 +1,10 @@
#!/bin/sh
+# jenkins build helper script for osmocom-bb. This is how we build on jenkins.osmocom.org
+#
+# environment variables:
+# * WITH_MANUALS: build manual PDFs if set to "1"
+# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
+#
set -ex
@@ -28,11 +34,44 @@ echo
set -x
-for dir in gprsdecode gsmmap layer23 osmocon trxcon virt_phy; do
+# building those sub-projects where 'distcheck' is known-working
+for dir in gprsdecode layer23; do
+ cd $base/src/host/$dir
+ autoreconf -fi
+ ./configure
+ make distcheck
+done
+
+# TODO: make sure 'distcheck' passes also for these
+for dir in gsmmap osmocon trxcon virt_phy; do
cd $base/src/host/$dir
autoreconf -fi
./configure
make
done
+# Build and publish manuals
+if [ "$WITH_MANUALS" = "1" ]; then
+ make -C "$base/doc/manuals"
+ make -C "$base/doc/manuals" check
+
+ if [ "$PUBLISH" = "1" ]; then
+ make -C "$base/doc/manuals" publish
+ fi
+fi
+
+# Test 'maintainer-clean'
+for dir in gprsdecode layer23 gsmmap 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/doc/examples/mobile/default.cfg b/doc/examples/mobile/default.cfg
index cc816305..f14e9009 100644
--- a/doc/examples/mobile/default.cfg
+++ b/doc/examples/mobile/default.cfg
@@ -59,4 +59,6 @@ ms 1
ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
no barred-access
rplmn 001 01
+ audio
+ io-handler none
no shutdown
diff --git a/doc/examples/mobile/multi_ms.cfg b/doc/examples/mobile/multi_ms.cfg
index bef2406e..c72817fd 100644
--- a/doc/examples/mobile/multi_ms.cfg
+++ b/doc/examples/mobile/multi_ms.cfg
@@ -59,6 +59,8 @@ ms one
ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
no barred-access
rplmn 001 01
+ audio
+ io-handler none
no shutdown
!
ms two
@@ -109,4 +111,6 @@ ms two
ki comp128 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
no barred-access
rplmn 001 01
+ audio
+ io-handler none
no shutdown
diff --git a/doc/manuals/Makefile b/doc/manuals/Makefile
new file mode 100644
index 00000000..603f4ba6
--- /dev/null
+++ b/doc/manuals/Makefile
@@ -0,0 +1,8 @@
+OSMO_GSM_MANUALS_DIR := $(shell ./osmo-gsm-manuals-dir.sh)
+srcdir=$(CURDIR)
+
+ASCIIDOC = osmocombb-usermanual.adoc
+ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
+include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
+
+include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
diff --git a/doc/manuals/chapters/scripting.adoc b/doc/manuals/chapters/scripting.adoc
new file mode 100644
index 00000000..8828a72f
--- /dev/null
+++ b/doc/manuals/chapters/scripting.adoc
@@ -0,0 +1,111 @@
+[[scripting]]
+== Scripting using Lua
+
+The mobile application can be extended using the
+https://www.lua.org/manual/5.3/[lua5.3 language].
+To use the scripting facility a script needs to be
+configured through the VTY interface and will be
+associated to a Mobile Station (MS). The script will
+then be able to interact with the specific MS.
+
+An event based programming model is to be used. This
+means that once the script has been loaded it should
+register to the wanted events, configure timers and
+return. When an event occurs the registered event
+handler will be executed.
+
+The following describes the exported runtime services
+to be used in the script.
+
+=== Logging
+
+The logging functions allow to generate log messages
+for different levels. The log implementatiom is using
+the standard Osmocom logging framework which allows to
+have multiple log targets, e.g. syslog, file or through
+the VTY.
+
+|========
+|Code |Return | Explanation
+|print(...) |void | Print a message with log level 'debug'
+|log_debug(...) |void | Print a message with log level 'debug'
+|log_notice(...) |void | Print a message with log level 'notice'
+|log_error(...) |void | Print a message with log level 'error'
+|log_fatal(...) |void | Print a message with log level 'fatal'
+|========
+
+==== Examples
+
+----
+Code:
+print("Log level 'debug'")
+log_debug("Log level 'debug'")
+log_notice("Log level 'notice'")
+log_error("Log level 'error'")
+log_fatal("Log level 'fatal'")
+
+Output:
+\<0011> @script.lua:1 Log level 'debug'
+\<0011> @script.lua:2 Log level 'debug'
+\<0011> @script.lua:3 Log level 'notice'
+\<0011> @script.lua:4 Log level 'error'
+\<0011> @script.lua:5 Log level 'fatal'
+
+----
+
+=== Timer class
+
+The timer allows to invoke a function once after the requested
+timeout. The timer creation function will return immediately and
+the callback will be called after the timeout and when no other
+lua code is executing. The _osmo.timeout_ function should be used
+to create a new time, a running timer can be canneled using the _cancel_
+method.
+
+|========
+|Code |Return |Explanation
+|osmo.timeout(timeout, cb)|A new timer|Create a new non-recurring timer. Timeout should be in rounded seconds and cb should be a function.
+|timer.cancel() |Void |Cancel the timer, the callback will not be called.
+|========
+
+==== Examples
+
+----
+Code:
+local timer = osmo.timeout(timeout_in_seconds, call_back)
+timer:cancel()
+----
+
+----
+Code:
+local timer = osmo.timeout(3, function()
+ print("Timeout passed")
+end)
+print("Configured")
+
+Output:
+\<0011> @script.lua:4 Configured
+\<0011> @script.lua:2 Timeout passed
+----
+
+=== MS class
+
+The MS singletong provides access to the Mobile Station configuration
+the script is associated with. This includes runtime information like
+the IMSI, IMEI or functions like start/stop.
+
+|========
+|Code |Return |Explanation
+|osmo.ms().imsi() |string |The IMSI. It might be invalid in case it has not been read from SIM card yet
+|osmo.ms().imei() |string |The configured IMEI
+|========
+==== Examples
+
+-----
+Code:
+local ms = osmo.ms()
+print(ms.imei(), ms.imsi())
+
+Output:
+\<0011> @script.lua:2 126000000000000
+-----
diff --git a/doc/manuals/osmo-gsm-manuals-dir.sh b/doc/manuals/osmo-gsm-manuals-dir.sh
new file mode 100755
index 00000000..f132eaae
--- /dev/null
+++ b/doc/manuals/osmo-gsm-manuals-dir.sh
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+# Find OSMO_GSM_MANUALS_DIR and print it to stdout. Print where it was taken from to stderr.
+
+# Find it in env, pkg-conf and ../../../osmo-gsm-manuals
+RET="$OSMO_GSM_MANUALS_DIR"
+if [ -n "$RET" ]; then
+ RET="$(realpath $RET)"
+ echo "OSMO_GSM_MANUALS_DIR: $RET (from env)" >&2
+else
+ RET="$(pkg-config osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null || true)"
+ if [ -n "$RET" ]; then
+ echo "OSMO_GSM_MANUALS_DIR: $RET (from pkg-conf)" >&2
+ else
+ RET="$(realpath $(realpath $(dirname $0))/../../../osmo-gsm-manuals)"
+ echo "OSMO_GSM_MANUALS_DIR: $RET (fallback)" >&2
+ fi
+fi
+
+# Print the result or error message
+if [ -d "$RET" ]; then
+ echo "$RET"
+else
+ echo "ERROR: OSMO_GSM_MANUALS_DIR does not exist!" >&2
+ echo "Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR." >&2
+ exit 1
+fi
diff --git a/doc/manuals/osmocombb-usermanual-docinfo.xml b/doc/manuals/osmocombb-usermanual-docinfo.xml
new file mode 100644
index 00000000..992a42e3
--- /dev/null
+++ b/doc/manuals/osmocombb-usermanual-docinfo.xml
@@ -0,0 +1,57 @@
+<revhistory>
+ <revision>
+ <revnumber>1</revnumber>
+ <date>November 2017</date>
+ <authorinitials>HHPF</authorinitials>
+ <revremark>
+ Initial version.
+ </revremark>
+ </revision>
+ </revhistory>
+
+<authorgroup>
+ <author>
+ <firstname>Harald</firstname>
+ <surname>Welte</surname>
+ <email>hwelte@sysmocom.de</email>
+ <authorinitials>HW</authorinitials>
+ <affiliation>
+ <shortaffil>sysmocom</shortaffil>
+ <orgname>sysmocom - s.f.m.c. GmbH</orgname>
+ <jobtitle>Managing Director</jobtitle>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Holger</firstname>
+ <surname>Freyther</surname>
+ <email>hfreyther@sysmocom.de</email>
+ <authorinitials>HHPF</authorinitials>
+ <affiliation>
+ <shortaffil>sysmocom</shortaffil>
+ <orgname>sysmocom - s.f.m.c. GmbH</orgname>
+ <jobtitle>Co-Founder</jobtitle>
+ </affiliation>
+ </author>
+</authorgroup>
+
+<copyright>
+ <year>2013-2017</year>
+ <holder>sysmocom - s.f.m.c. GmbH</holder>
+</copyright>
+
+<legalnotice>
+ <para>
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with no Invariant Sections, no Front-Cover Texts,
+ and no Back-Cover Texts. A copy of the license is included in
+ the section entitled "GNU Free Documentation License".
+ </para>
+ <para>
+ The Asciidoc source code of this manual can be found at
+ <ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
+ http://git.osmocom.org/osmo-gsm-manuals/
+ </ulink>
+ </para>
+</legalnotice>
diff --git a/doc/manuals/osmocombb-usermanual.adoc b/doc/manuals/osmocombb-usermanual.adoc
new file mode 100644
index 00000000..024fc79a
--- /dev/null
+++ b/doc/manuals/osmocombb-usermanual.adoc
@@ -0,0 +1,21 @@
+OsmocomBB User Manual
+=====================
+Holger Hans Peter Freyther <holger@moiji-mobile.com>
+
+
+include::./common/chapters/preface.adoc[]
+
+include::{srcdir}/chapters/scripting.adoc[]
+
+include::./common/chapters/vty.adoc[]
+
+include::./common/chapters/logging.adoc[]
+
+
+include::./common/chapters/port_numbers.adoc[]
+
+include::./common/chapters/bibliography.adoc[]
+
+include::./common/chapters/glossary.adoc[]
+
+include::./common/chapters/gfdl.adoc[]
diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h
index c6156f5a..cf41ac74 100644
--- a/include/l1ctl_proto.h
+++ b/include/l1ctl_proto.h
@@ -64,6 +64,9 @@ enum {
L1CTL_DATA_TBF_REQ,
L1CTL_DATA_TBF_CONF,
+
+ /* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */
+ L1CTL_EXT_RACH_REQ,
};
enum ccch_mode {
@@ -146,11 +149,24 @@ 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 */
+ uint8_t padding[1];
} __attribute__((packed));
/* data on the CCCH was found. This is following the header */
@@ -228,7 +244,8 @@ 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 */
+ uint8_t padding[1];
} __attribute__((packed));
/* the l1_info_ul header is in front */
@@ -238,6 +255,15 @@ struct l1ctl_rach_req {
uint16_t offset;
} __attribute__((packed));
+
+/* the l1_info_ul header is in front */
+struct l1ctl_ext_rach_req {
+ uint16_t ra11;
+ uint8_t synch_seq;
+ uint8_t combined;
+ uint16_t offset;
+} __attribute__((packed));
+
/* the l1_info_ul header is in front */
struct l1ctl_par_req {
int8_t ta;
diff --git a/src/host/fb_tools/bdf_to_c.py b/src/host/fb_tools/bdf_to_c.py
index 86be6a6b..ebeb7f9c 100755
--- a/src/host/fb_tools/bdf_to_c.py
+++ b/src/host/fb_tools/bdf_to_c.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
@@ -133,7 +133,7 @@ class BDF_Font(object) :
arr = l.split(None,1)
if len(arr) < 2 and \
arr[0].lower() != 'STARTCHAR' :
- print >>sys.stderr,'Not start of glyph: %s'%(l)
+ print('Not start of glyph: %s'%(l), file=sys.stderr)
continue
inchar = unique_name(arr[1],self.glyphs)
continue
@@ -159,7 +159,7 @@ class BDF_Font(object) :
continue
if len(arr) < 2 :
- print >>sys.stderr,'Bad line in font: %s'%(l)
+ print('Bad line in font: %s'%(l), file=sys.stderr)
continue
data[arr[0].lower()] = arr[1]
@@ -183,40 +183,40 @@ if __name__ == '__main__' :
if opts.firstchar == None :
opts.firstchar = min(font.enc)
- print 'First character in font: %d, %s'%(opts.firstchar,
- font.enc[opts.firstchar])
+ print('First character in font: %d, %s'%(opts.firstchar,
+ font.enc[opts.firstchar]))
if opts.lastchar == None :
opts.lastchar = max(font.enc)
- print 'Last character in font: %d, %s'%(opts.lastchar,
- font.enc[opts.lastchar])
+ print('Last character in font: %d, %s'%(opts.lastchar,
+ font.enc[opts.lastchar]))
if opts.base == None :
opts.base = 'font_'+os.path.basename(args[0])
if opts.base[-4:] == '.bdf' :
opts.base = opts.base[:-4]
- print >>sys.stderr,'Guessing symbol prefix to be %s.'%(opts.base)
+ print('Guessing symbol prefix to be %s.'%(opts.base), file=sys.stderr)
if opts.out == None :
opts.out = os.path.basename(args[0])
if opts.out[-4:] == '.bdf' :
opts.out = opts.out[:-4]
opts.out = opts.out + '.c'
- print >>sys.stderr,'Guessing output filename to be %s.'%(opts.out)
+ print('Guessing output filename to be %s.'%(opts.out), file=sys.stderr)
if os.path.exists(opts.out) :
- print >>sys.stderr,'Will *NOT* overwrite existing file when guessing output!'
+ print('Will *NOT* overwrite existing file when guessing output!', file=sys.stderr)
sys.exit(1)
of = file(opts.out,'w')
- print >>of,'#include <fb/font.h>'
- print >>of,'/* file autogenerated by %s */'%(sys.argv[0])
+ print('#include <fb/font.h>', file=of)
+ print('/* file autogenerated by %s */' %(sys.argv[0]), file=of)
offsets = list()
glyphnames = list()
- print >>of,'static const uint8_t %s_data[] = {'%(opts.base)
+ print('static const uint8_t %s_data[] = {'%(opts.base), file=of)
pos = 0
@@ -236,8 +236,8 @@ if __name__ == '__main__' :
bitmap = glyph['bitmap']
if bbx[1] != len(bitmap) :
- print >>sys.stderr,'ERROR: glyph',charname,'has wrong number of lines of data!'
- print >>sys.stderr,' want: ',bbx[1],'but have',len(bitmap)
+ print('ERROR: glyph',charname,'has wrong number of lines of data!', file=sys.stderr)
+ print(' want: ',bbx[1],'but have',len(bitmap), file=sys.stderr)
sys.exit(1)
removedrows = 0
@@ -254,40 +254,40 @@ if __name__ == '__main__' :
bitmap = bitmap[:-1]
if removedrows > 0 :
- print "Glyph %s: removed %d rows."%(charname,removedrows)
+ print("Glyph %s: removed %d rows."%(charname,removedrows))
w = int(glyph['dwidth'].split(None,1)[0])
- print >>of,'/* --- new character %s %s starting at offset 0x%04x --- */'%(
- charname,ascii_charnum(i),pos)
- print >>of,'\t/*%04x:*/\t%d, %d, %d, %d, %d, /* width and bbox (w,h,x,y) */'%(
- pos,w,bbx[0],bbx[1],bbx[2],bbx[3])
+ print('/* --- new character %s %s starting at offset 0x%04x --- */'%(
+ charname,ascii_charnum(i),pos), file=of)
+ print('\t/*%04x:*/\t%d, %d, %d, %d, %d, /* width and bbox (w,h,x,y) */'%(
+ pos,w,bbx[0],bbx[1],bbx[2],bbx[3]), file=of)
pos += 5
for k,l in enumerate(bitmap) :
bytes = [ int(l[i:i+2],16) for i in range(0,len(l),2) ]
if len(bytes) != (bbx[0]+7)/8 :
- print >>sys.stderr,'ERROR: glyph',charname,'has wrong # of bytes'
- print >>sys.stderr,' per line. Want',(bbx[0]+7)/8,'have',len(bytes)
+ print('ERROR: glyph',charname,'has wrong # of bytes', file=sys.stderr)
+ print(' per line. Want',(bbx[0]+7)/8,'have',len(bytes), file=sys.stderr)
sys.exit(1)
cdata = ','.join([ '0x%02x'%v for v in bytes ])
comment = ''.join([ byte_to_bits(b) for b in bytes ])
- print >>of,'\t/*%04x:*/\t'%(pos)+cdata+', /* '+comment+' */'
+ print('\t/*%04x:*/\t'%(pos)+cdata+', /* '+comment+' */', file=of)
pos += len(bytes)
- print >>of,"};"
+ print("};", file=of)
x = ',\n\t'.join(['0x%04x /* %s */'%(w,n) for w,n in zip(offsets,glyphnames)])
- print >>of,'static const uint16_t %s_offsets[] = {\n\t%s\n};'%(opts.base,x)
+ print('static const uint16_t %s_offsets[] = {\n\t%s\n};'%(opts.base,x), file=of)
height = font.ascent + font.descent
- print >>of,'const struct fb_font %s = {'%(opts.base)
- print >>of,'\t.height = %d,'%(height)
- print >>of,'\t.ascent = %d,'%(font.ascent)
- print >>of,'\t.firstchar = %d, /* %s */'%(opts.firstchar,font.enc.get(opts.firstchar,"?"))
- print >>of,'\t.lastchar = %d, /* %s */'%(opts.lastchar,font.enc.get(opts.lastchar,"?"))
- print >>of,'\t.chardata = %s_data,'%(opts.base)
- print >>of,'\t.charoffs = %s_offsets,'%(opts.base)
- print >>of,'};'
+ print('const struct fb_font %s = {'%(opts.base), file=of)
+ print('\t.height = %d,'%(height), file=of)
+ print('\t.ascent = %d,'%(font.ascent), file=of)
+ print('\t.firstchar = %d, /* %s */'%(opts.firstchar,font.enc.get(opts.firstchar,"?")), file=of)
+ print('\t.lastchar = %d, /* %s */'%(opts.lastchar,font.enc.get(opts.lastchar,"?")), file=of)
+ print('\t.chardata = %s_data,'%(opts.base), file=of)
+ print('\t.charoffs = %s_offsets,'%(opts.base), file=of)
+ print('};', file=of)
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/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am
index cd3437e3..f9364cd9 100644
--- a/src/host/layer23/include/osmocom/bb/common/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am
@@ -1,2 +1,3 @@
noinst_HEADERS = l1ctl.h l1l2_interface.h l23_app.h logging.h \
- networks.h gps.h sysinfo.h osmocom_data.h utils.h
+ networks.h gps.h sysinfo.h osmocom_data.h utils.h \
+ sap_proto.h sap_fsm.h sap_interface.h sim.h
diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h
index e4dbdedc..02ffa343 100644
--- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h
+++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h
@@ -49,7 +49,7 @@ int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode);
/* Transmit TCH_MODE_REQ */
int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode,
- uint8_t audio_mode);
+ uint8_t audio_mode, uint8_t tch_loop_mode);
/* Transmit ECHO_REQ */
int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len);
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 486c36d0..14e594cb 100644
--- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
+++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h
@@ -13,6 +13,7 @@ struct osmocom_ms;
#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>
@@ -23,9 +24,16 @@ struct osmocom_ms;
#include <osmocom/bb/common/l1ctl.h>
struct osmosap_entity {
- osmosap_cb_t msg_handler;
- uint8_t sap_state;
+ 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 {
diff --git a/src/host/layer23/include/osmocom/bb/common/sap_fsm.h b/src/host/layer23/include/osmocom/bb/common/sap_fsm.h
new file mode 100644
index 00000000..d79bc1cc
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/sap_fsm.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <osmocom/bb/common/osmocom_data.h>
+
+/* How long should we wait for connection establishment */
+#define SAP_FSM_CONN_EST_TIMEOUT 5
+#define SAP_FSM_CONN_EST_T 0
+
+/* How long should we wait for connection release */
+#define SAP_FSM_CONN_REL_TIMEOUT 3
+#define SAP_FSM_CONN_REL_T 1
+
+/* How long should we wait for request to complete */
+#define SAP_FSM_PROC_REQ_TIMEOUT 5
+#define SAP_FSM_PROC_REQ_T 2
+
+#define SAP_STATE_IS_ACTIVE(state) \
+ (state >= SAP_STATE_WAIT_FOR_CARD)
+
+enum sap_fsm_state {
+ SAP_STATE_NOT_CONNECTED = 0,
+ SAP_STATE_CONNECTING,
+ SAP_STATE_DISCONNECTING, /* Auxiliary state (not from specs) */
+ SAP_STATE_WAIT_FOR_CARD, /* Auxiliary state (not from specs) */
+ SAP_STATE_IDLE,
+ SAP_STATE_PROC_ATR_REQ,
+ SAP_STATE_PROC_APDU_REQ,
+ SAP_STATE_PROC_RESET_REQ,
+ SAP_STATE_PROC_STATUS_REQ,
+ SAP_STATE_PROC_SET_TP_REQ,
+ SAP_STATE_PROC_POWERON_REQ,
+ SAP_STATE_PROC_POWEROFF_REQ,
+};
+
+int sap_fsm_alloc(struct osmocom_ms *ms);
diff --git a/src/host/layer23/include/osmocom/bb/common/sap_interface.h b/src/host/layer23/include/osmocom/bb/common/sap_interface.h
index e4e64cef..87a0f851 100644
--- a/src/host/layer23/include/osmocom/bb/common/sap_interface.h
+++ b/src/host/layer23/include/osmocom/bb/common/sap_interface.h
@@ -1,75 +1,23 @@
-#ifndef _SAP_INTERFACE_H
-#define _SAP_INTERFACE_H
+#pragma once
-typedef int (*osmosap_cb_t)(struct msgb *msg, struct osmocom_ms *ms);
+#include <stdint.h>
-int sap_open(struct osmocom_ms *ms, const char *socket_path);
-int sap_close(struct osmocom_ms *ms);
-int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length);
-int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb);
-int osmosap_sapsocket(struct osmocom_ms *ms, const char *path);
-int osmosap_init(struct osmocom_ms *ms);
-
-enum osmosap_state {
- SAP_SOCKET_ERROR,
- SAP_NOT_CONNECTED,
- SAP_IDLE,
- SAP_CONNECTION_UNDER_NEGOTIATION,
- SAP_PROCESSING_ATR_REQUEST,
- SAP_PROCESSING_APDU_REQUEST
-};
+#include <osmocom/core/msgb.h>
-/* BTSAP 1.13 */
-enum osmosap_msg_type {
- SAP_CONNECT_REQ = 0x00,
- SAP_CONNECT_RESP = 0x01,
- SAP_DISCONNECT_REQ = 0x02,
- SAP_DISCONNECT_RESP = 0x03,
- SAP_DISCONNECT_IND = 0x04,
- SAP_TRANSFER_APDU_REQ = 0x05,
- SAP_TRANSFER_APDU_RESP = 0x06,
- SAP_TRANSFER_ATR_REQ = 0x07,
- SAP_TRANSFER_ATR_RESP = 0x08,
- SAP_POWER_SIM_OFF_REQ = 0x09,
- SAP_POWER_SIM_OFF_RESP = 0x0A,
- SAP_POWER_SIM_ON_REQ = 0x0B,
- SAP_POWER_SIM_ON_RESP = 0x0C,
- SAP_RESET_SIM_REQ = 0x0D,
- SAP_RESET_SIM_RESP = 0x0E,
- SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F,
- SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10,
- SAP_STATUS_IND = 0x11,
- SAP_ERROR_RESP = 0x12,
- SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13,
- SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14
-};
+struct osmocom_ms;
-/* BTSAP 5.2 */
-enum osmosap_param_type {
- SAP_MAX_MSG_SIZE = 0x00,
- SAP_CONNECTION_STATUS = 0x01,
- SAP_RESULT_CODE = 0x02,
- SAP_DISCONNECTION_TYPE = 0x03,
- SAP_COMMAND_APDU = 0x04,
- SAP_COMMAND_APDU_7816 = 0x10,
- SAP_RESPONSE_APDU = 0x05,
- SAP_ATR = 0x06,
- SAP_CARD_READER_STATUS = 0x07,
- SAP_STATUS_CHANGE = 0x08,
- SAP_TRANSPORT_PROTOCOL = 0x09
-};
+typedef int (*sap_msg_cb_t)(struct osmocom_ms *ms, struct msgb *msg);
+typedef int (*sap_rsp_cb_t)(struct osmocom_ms *ms, int res_code,
+ uint8_t res_type, uint16_t param_len, const uint8_t *param_val);
-struct sap_param {
- uint8_t id;
- uint16_t len;
- uint8_t *value;
-};
-
-struct sap_msg {
- uint8_t id;
- uint8_t num_params;
- struct sap_param *params;
-};
+void sap_init(struct osmocom_ms *ms);
+int sap_open(struct osmocom_ms *ms);
+int sap_close(struct osmocom_ms *ms);
+int _sap_close_sock(struct osmocom_ms *ms);
+int sap_send_reset_req(struct osmocom_ms *ms);
+int sap_send_poweron_req(struct osmocom_ms *ms);
+int sap_send_poweroff_req(struct osmocom_ms *ms);
-#endif /* _SAP_INTERFACE_H */
+int sap_send_atr_req(struct osmocom_ms *ms);
+int sap_send_apdu(struct osmocom_ms *ms, uint8_t *apdu, uint16_t apdu_len);
diff --git a/src/host/layer23/include/osmocom/bb/common/sap_proto.h b/src/host/layer23/include/osmocom/bb/common/sap_proto.h
new file mode 100644
index 00000000..e149f00d
--- /dev/null
+++ b/src/host/layer23/include/osmocom/bb/common/sap_proto.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+/* Table 5.1: Message Overview
+ * NOTE: messages are used as events for SAP FSM */
+enum sap_msg_type {
+ SAP_CONNECT_REQ = 0x00,
+ SAP_CONNECT_RESP = 0x01,
+ SAP_DISCONNECT_REQ = 0x02,
+ SAP_DISCONNECT_RESP = 0x03,
+ SAP_DISCONNECT_IND = 0x04,
+ SAP_TRANSFER_APDU_REQ = 0x05,
+ SAP_TRANSFER_APDU_RESP = 0x06,
+ SAP_TRANSFER_ATR_REQ = 0x07,
+ SAP_TRANSFER_ATR_RESP = 0x08,
+ SAP_POWER_SIM_OFF_REQ = 0x09,
+ SAP_POWER_SIM_OFF_RESP = 0x0A,
+ SAP_POWER_SIM_ON_REQ = 0x0B,
+ SAP_POWER_SIM_ON_RESP = 0x0C,
+ SAP_RESET_SIM_REQ = 0x0D,
+ SAP_RESET_SIM_RESP = 0x0E,
+ SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F,
+ SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10,
+ SAP_STATUS_IND = 0x11,
+ SAP_ERROR_RESP = 0x12,
+ SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13,
+ SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14
+};
+
+/* Table 5.15: List of Parameter IDs */
+enum sap_param_type {
+ SAP_MAX_MSG_SIZE = 0x00,
+ SAP_CONNECTION_STATUS = 0x01,
+ SAP_RESULT_CODE = 0x02,
+ SAP_DISCONNECTION_TYPE = 0x03,
+ SAP_COMMAND_APDU = 0x04,
+ SAP_COMMAND_APDU_7816 = 0x10,
+ SAP_RESPONSE_APDU = 0x05,
+ SAP_ATR = 0x06,
+ SAP_CARD_READER_STATUS = 0x07,
+ SAP_STATUS_CHANGE = 0x08,
+ SAP_TRANSPORT_PROTOCOL = 0x09
+};
+
+/* Table 5.18: Possible values for ResultCode */
+enum sap_result_type {
+ SAP_RESULT_OK_REQ_PROC_CORR = 0x00,
+ SAP_RESULT_ERROR_NO_REASON = 0x01,
+ SAP_RESULT_ERROR_CARD_NOT_ACC = 0x02,
+ SAP_RESULT_ERROR_CARD_POWERED_OFF = 0x03,
+ SAP_RESULT_ERROR_CARD_REMOVED = 0x04,
+ SAP_RESULT_ERROR_CARD_POWERED_ON = 0x05,
+ SAP_RESULT_ERROR_DATA_UNAVAIL = 0x06,
+ SAP_RESULT_ERROR_NOT_SUPPORTED = 0x07,
+};
+
+/* Table 5.19: Possible values for StatusChange */
+enum sap_card_status_type {
+ SAP_CARD_STATUS_UNKNOWN_ERROR = 0x00,
+ SAP_CARD_STATUS_RESET = 0x01,
+ SAP_CARD_STATUS_NOT_ACC = 0x02,
+ SAP_CARD_STATUS_REMOVED = 0x03,
+ SAP_CARD_STATUS_INSERTED = 0x04,
+ SAP_CARD_STATUS_RECOVERED = 0x05,
+};
+
+/* Table 5.16: Possible values for ConnectionStatus */
+enum sap_conn_status_type {
+ SAP_CONN_STATUS_OK_READY = 0x00,
+ SAP_CONN_STATUS_ERROR_CONN = 0x01,
+ SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE = 0x02,
+ SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE = 0x03,
+ SAP_CONN_STATUS_OK_CALL = 0x04,
+};
+
+extern const struct value_string sap_msg_names[];
+extern const struct value_string sap_param_names[];
+extern const struct value_string sap_result_names[];
+extern const struct value_string sap_card_status_names[];
+extern const struct value_string sap_conn_status_names[];
+
+/* Figure 5.2: Payload Coding */
+struct sap_param {
+ /* Parameter ID, see sap_param_type enum */
+ uint8_t param_id;
+ /* Reserved for further use (shall be set to 0x00) */
+ uint8_t reserved[1];
+ /* Parameter length */
+ uint16_t length;
+ /* Parameter value (and optional padding) */
+ uint8_t value[0];
+} __attribute__((packed));
+
+/* Figure 5.1 Message Format */
+struct sap_message {
+ /* Message ID, see sap_msg_type enum */
+ uint8_t msg_id;
+ /* Number of parameters */
+ uint8_t num_params;
+ /* Reserved for further use (shall be set to 0x00) */
+ uint8_t reserved[2];
+ /* Payload, see sap_param struct */
+ uint8_t payload[0];
+} __attribute__((packed));
+
+#define GSM_SAP_LENGTH 300
+#define GSM_SAP_HEADROOM 32
+
+struct msgb *sap_msgb_alloc(uint8_t msg_id);
+struct msgb *sap_msg_parse(const uint8_t *buf, size_t buf_len, int max_msg_size);
+int sap_check_result_code(const struct sap_message *sap_msg);
+
+void sap_msgb_add_param(struct msgb *msg,
+ enum sap_param_type param_type,
+ uint16_t param_len, const uint8_t *param_value);
+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/include/osmocom/bb/common/sysinfo.h b/src/host/layer23/include/osmocom/bb/common/sysinfo.h
index f843f271..7fea2daa 100644
--- a/src/host/layer23/include/osmocom/bb/common/sysinfo.h
+++ b/src/host/layer23/include/osmocom/bb/common/sysinfo.h
@@ -17,7 +17,7 @@
#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 */
+/* structure of all received system information */
struct gsm48_sysinfo {
/* flags of available information */
uint8_t si1, si2, si2bis, si2ter, si3,
diff --git a/src/host/layer23/include/osmocom/bb/misc/Makefile.am b/src/host/layer23/include/osmocom/bb/misc/Makefile.am
index 71c9d389..59760906 100644
--- a/src/host/layer23/include/osmocom/bb/misc/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/misc/Makefile.am
@@ -1 +1 @@
-noinst_HEADERS = layer3.h rslms.h
+noinst_HEADERS = layer3.h rslms.h cell_log.h
diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am
index 8e4be1ac..623964fd 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am
+++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am
@@ -1,3 +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
+ transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \
+ app_mobile.h voice.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 c2ab3c88..191f4baf 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h
@@ -9,7 +9,7 @@ struct osmocom_ms;
struct vty;
int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
- const char *config_file, const char *vty_ip, uint16_t vty_port);
+ const char *config_file);
int l23_app_exit(void);
int l23_app_work(int *quit);
int mobile_delete(struct osmocom_ms *ms, int force);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
index d4caac99..b332778d 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h
@@ -6,7 +6,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.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 +15,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
@@ -129,9 +129,9 @@ struct gsm322_plmn {
/* 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 +144,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;
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..bf3aa257 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h
@@ -55,13 +55,13 @@
/* MMxx-SAP header */
struct gsm48_mmxx_hdr {
- int msg_type; /* MMxx_* primitive */
+ 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 */
-};
+} __attribute__((packed));
/* GSM 6.1.2 */
#define GSM48_MMR_REG_REQ 0x01
@@ -71,10 +71,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
@@ -140,7 +139,7 @@ struct gsm48_mm_event {
uint32_t msg_type;
uint8_t sres[4];
-};
+} __attribute__((packed));
/* GSM 04.08 MM timers */
#define GSM_T3210_MS 20, 0
@@ -204,7 +203,7 @@ struct gsm48_mm_conn {
struct llist_head list;
struct gsm48_mmlayer *mm;
- /* ref and type form a unique tupple */
+ /* ref and type form a unique tuple */
uint32_t ref; /* reference to trans */
uint8_t protocol;
uint8_t transaction_id;
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 6235bfdb..9b499a6b 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h
@@ -61,7 +61,7 @@ struct gsm48_rr_hdr {
uint32_t msg_type; /* RR-* primitive */
uint8_t sapi;
uint8_t cause;
-};
+} __attribute__((packed));
/* GSM 04.07 9.1.1 */
#define GSM48_RR_ST_IDLE 0
@@ -188,6 +188,9 @@ 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;
diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc.h b/src/host/layer23/include/osmocom/bb/mobile/mncc.h
index 8ec9358d..5e976cc2 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/mncc.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/mncc.h
@@ -58,7 +58,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
@@ -136,7 +136,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;
diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h
index 4e5d5a19..57f23ee5 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/settings.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/settings.h
@@ -3,10 +3,29 @@
#define MOB_C7_DEFLT_ANY_TIMEOUT 30
+/* TCH frame I/O handler */
+enum audio_io_handler {
+ /* No handler, drop frames */
+ AUDIO_IOH_NONE = 0,
+ /* Return to sender */
+ AUDIO_IOH_LOOPBACK,
+};
+
+extern const struct value_string audio_io_handler_names[];
+static inline const char *audio_io_handler_name(enum audio_io_handler val)
+{ return get_value_string(audio_io_handler_names, val); }
+
+struct audio_settings {
+ enum audio_io_handler io_handler;
+};
+
struct gsm_settings {
char layer2_socket_path[128];
char sap_socket_path[128];
+ /* Audio settings */
+ struct audio_settings audio;
+
/* IMEI */
char imei[16];
char imeisv[17];
diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
index ac785d4a..698b0fdc 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h
@@ -20,9 +20,12 @@ struct gsm_sub_plmn_na {
#define GSM_IMSI_LENGTH 16
+#define GSM_SIM_IS_READER(type) \
+ (type == GSM_SIM_TYPE_L1PHY || type == GSM_SIM_TYPE_SAP)
+
enum {
GSM_SIM_TYPE_NONE = 0,
- GSM_SIM_TYPE_READER,
+ GSM_SIM_TYPE_L1PHY,
GSM_SIM_TYPE_TEST,
GSM_SIM_TYPE_SAP
};
@@ -87,6 +90,8 @@ 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_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);
diff --git a/src/host/layer23/include/osmocom/bb/mobile/vty.h b/src/host/layer23/include/osmocom/bb/mobile/vty.h
index 3bec1139..d0668041 100644
--- a/src/host/layer23/include/osmocom/bb/mobile/vty.h
+++ b/src/host/layer23/include/osmocom/bb/mobile/vty.h
@@ -10,6 +10,7 @@ enum ms_vty_node {
MS_NODE = _LAST_OSMOVTY_NODE + 1,
TESTSIM_NODE,
SUPPORT_NODE,
+ AUDIO_NODE,
};
int ms_vty_go_parent(struct vty *vty);
diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am
index b76094c6..a8d9f7e7 100644
--- a/src/host/layer23/src/common/Makefile.am
+++ b/src/host/layer23/src/common/Makefile.am
@@ -2,5 +2,5 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS)
noinst_LIBRARIES = liblayer23.a
-liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_interface.c \
+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
diff --git a/src/host/layer23/src/common/gps.c b/src/host/layer23/src/common/gps.c
index 35ee4167..5225fe0e 100644
--- a/src/host/layer23/src/common/gps.c
+++ b/src/host/layer23/src/common/gps.c
@@ -82,7 +82,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 +106,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 +137,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 +149,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 +316,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..da30767c 100644
--- a/src/host/layer23/src/common/l1ctl.c
+++ b/src/host/layer23/src/common/l1ctl.c
@@ -50,6 +50,79 @@
extern struct gsmtap_inst *gsmtap_inst;
+#define CB_FCCH -1
+#define CB_SCH -2
+#define CB_BCCH -3
+#define CB_IDLE -4
+
+/* according to TS 05.02 Clause 7 Table 3 of 9 an Figure 8a */
+static const int ccch_block_table[51] = {
+ CB_FCCH, CB_SCH,/* 0..1 */
+ CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */
+ 0, 0, 0, 0, /* 6..9: B0 */
+ CB_FCCH, CB_SCH,/* 10..11 */
+ 1, 1, 1, 1, /* 12..15: B1 */
+ 2, 2, 2, 2, /* 16..19: B2 */
+ CB_FCCH, CB_SCH,/* 20..21 */
+ 3, 3, 3, 3, /* 22..25: B3 */
+ 4, 4, 4, 4, /* 26..29: B4 */
+ CB_FCCH, CB_SCH,/* 30..31 */
+ 5, 5, 5, 5, /* 32..35: B5 */
+ 6, 6, 6, 6, /* 36..39: B6 */
+ CB_FCCH, CB_SCH,/* 40..41 */
+ 7, 7, 7, 7, /* 42..45: B7 */
+ 8, 8, 8, 8, /* 46..49: B8 */
+ -4 /* 50: Idle */
+};
+
+/* determine the CCCH block number based on the frame number */
+static unsigned int fn2ccch_block(uint32_t fn)
+{
+ int rc = ccch_block_table[fn%51];
+ /* 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_rsl2gsmtap(rsl_chantype, link_id);
+ 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)
{
struct l1ctl_hdr *l1h;
@@ -63,7 +136,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 +218,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)) {
@@ -229,11 +303,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(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)
@@ -298,8 +392,9 @@ 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));
+ gsmtap_send(gsmtap_inst, ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK,
+ chan_ts, gsmtap_chan_type, chan_ss, 0, 127, 255,
+ msg->l2h, msgb_l2len(msg));
/* prepend uplink info header */
l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul));
@@ -366,7 +461,7 @@ int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode)
/* Transmit L1CTL_TCH_MODE_REQ */
int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode,
- uint8_t audio_mode)
+ uint8_t audio_mode, uint8_t tch_loop_mode)
{
struct msgb *msg;
struct l1ctl_tch_mode_req *req;
@@ -380,6 +475,7 @@ 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_loop_mode = tch_loop_mode;
return osmo_send_l1(ms, msg);
}
@@ -636,7 +732,7 @@ static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg)
LOGP(DL1C, LOGL_INFO, "SIM %s\n", osmo_hexdump(data, len));
sim_apdu_resp(ms, msg);
-
+
return 0;
}
@@ -814,13 +910,6 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *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));
/* prepend uplink info header */
diff --git a/src/host/layer23/src/common/l1l2_interface.c b/src/host/layer23/src/common/l1l2_interface.c
index c07b0a1d..cd5f9106 100644
--- a/src/host/layer23/src/common/l1l2_interface.c
+++ b/src/host/layer23/src/common/l1l2_interface.c
@@ -62,6 +62,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;
}
diff --git a/src/host/layer23/src/common/main.c b/src/host/layer23/src/common/main.c
index 2920cd9e..9d1c69ee 100644
--- a/src/host/layer23/src/common/main.c
+++ b/src/host/layer23/src/common/main.c
@@ -208,7 +208,7 @@ 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);
@@ -258,10 +258,6 @@ int main(int argc, char **argv)
exit(1);
}
- rc = sap_open(ms, sap_socket_path);
- if (rc < 0)
- fprintf(stderr, "Failed during sap_open(), no SIM reader\n");
-
ms->lapdm_channel.lapdm_dcch.l1_ctx = ms;
ms->lapdm_channel.lapdm_dcch.l3_ctx = ms;
ms->lapdm_channel.lapdm_acch.l1_ctx = ms;
diff --git a/src/host/layer23/src/common/networks.c b/src/host/layer23/src/common/networks.c
index 40b70a10..b4757e96 100644
--- a/src/host/layer23/src/common/networks.c
+++ b/src/host/layer23/src/common/networks.c
@@ -1822,33 +1822,33 @@ int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi)
const char *gsm_print_mcc(uint16_t mcc)
{
- static char string[5] = "000";
+ static char string[6] = "000";
- snprintf(string, 4, "%03x", mcc);
+ snprintf(string, 5, "%03x", mcc);
return string;
}
const char *gsm_print_mnc(uint16_t mnc)
{
- static char string[7];
+ 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, 6, "0x%03x", mnc);
+ snprintf(string, 7, "0x%03x", mnc);
return string;
}
/* two digits */
if ((mnc & 0x000f) == 0x000f) {
- snprintf(string, 6, "%02x", mnc >> 4);
+ snprintf(string, 7, "%02x", mnc >> 4);
return string;
}
/* three digits */
- snprintf(string, 6, "%03x", mnc);
+ snprintf(string, 7, "%03x", mnc);
return string;
}
diff --git a/src/host/layer23/src/common/sap_fsm.c b/src/host/layer23/src/common/sap_fsm.c
new file mode 100644
index 00000000..22658917
--- /dev/null
+++ b/src/host/layer23/src/common/sap_fsm.c
@@ -0,0 +1,691 @@
+/*
+ * SAP (SIM Access Profile) FSM definition
+ * based on Bluetooth SAP specification
+ *
+ * (C) 2018-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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/logging.h>
+
+#include <osmocom/bb/common/sap_interface.h>
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/common/sap_fsm.h>
+
+/*! Send encoded SAP message to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \param[in] msg encoded SAP message buffer
+ * \returns 0 in case of success, negative in case of error
+ */
+static int sap_send_msgb(struct osmocom_ms *ms, struct msgb *msg)
+{
+ int rc;
+
+ rc = osmo_wqueue_enqueue(&ms->sap_wq, msg);
+ if (rc) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to enqueue SAP message\n");
+ msgb_free(msg);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void sap_fsm_disconnect(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
+
+static void sap_fsm_connect(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct msgb *msg;
+ uint16_t size;
+
+ /* Section 5.1.1, CONNECT_REQ */
+ msg = sap_msgb_alloc(SAP_CONNECT_REQ);
+ if (!msg)
+ return;
+
+ /* Section 4.1.1, start MaxMsgSize negotiation */
+ size = htons(ms->sap_entity.max_msg_size);
+ sap_msgb_add_param(msg, SAP_MAX_MSG_SIZE,
+ sizeof(size), (uint8_t *) &size);
+
+ sap_send_msgb(ms, msg);
+}
+
+static void sap_negotiate_msg_size(struct osmosap_entity *sap,
+ const struct sap_message *sap_msg)
+{
+ uint16_t size, param_len;
+ const char *cause = NULL;
+ struct sap_param *param;
+
+ param = sap_get_param(sap_msg, SAP_MAX_MSG_SIZE, &param_len);
+ if (!param) {
+ cause = "missing expected MaxMsgSize parameter";
+ goto error;
+ }
+ if (param_len != sizeof(size)) {
+ cause = "MaxMsgSize parameter has wrong length";
+ goto error;
+ }
+
+ /* Parse MaxMsgSize suggested by server */
+ size = osmo_load16be(param->value);
+ if (size > SAP_MAX_MSG_SIZE) {
+ cause = "suggested MaxMsgSize is too big for us";
+ goto error;
+ }
+
+ /* Attempt to initiate connection again */
+ sap->max_msg_size = size;
+ sap_fsm_connect(sap->fi, sap->fi->state);
+ return;
+
+error:
+ LOGP(DSAP, LOGL_ERROR, "MaxMsgSize negotiation failed: %s\n", cause);
+ osmo_fsm_inst_state_chg(sap->fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+}
+
+static void sap_fsm_conn_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct sap_message *sap_msg = (struct sap_message *) data;
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param;
+ uint16_t param_len;
+ uint8_t status;
+
+ /* Section 5.1.2, CONNECT_RESP */
+ param = sap_get_param(sap_msg, SAP_CONNECTION_STATUS, &param_len);
+ if (!param || param_len != sizeof(status)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory connection status\n");
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ return;
+ }
+
+ /* Parse connection status */
+ status = param->value[0];
+
+ LOGP(DSAP, LOGL_INFO, "SAP connection status (0x%02x): %s\n",
+ status, get_value_string(sap_conn_status_names, status));
+
+ switch ((enum sap_conn_status_type) status) {
+ case SAP_CONN_STATUS_OK_CALL:
+ ms->sap_entity.card_status = SAP_CARD_STATUS_NOT_ACC;
+ /* fall-through */
+ case SAP_CONN_STATUS_OK_READY:
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_WAIT_FOR_CARD, 0, 0);
+ break;
+
+ case SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE:
+ case SAP_CONN_STATUS_ERROR_CONN:
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ break;
+
+ /* Section 4.1.1, MaxMsgSize negotiation */
+ case SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE:
+ sap_negotiate_msg_size(&ms->sap_entity, sap_msg);
+ break;
+ }
+}
+
+static void sap_fsm_conn_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct msgb *msg;
+
+ LOGP(DSAP, LOGL_DEBUG, "Initiating connection release\n");
+
+ /* We don't care about possible allocating / sending errors */
+ msg = sap_msgb_alloc(SAP_DISCONNECT_REQ);
+ if (msg != NULL)
+ sap_send_msgb((struct osmocom_ms *) fi->priv, msg);
+}
+
+static void sap_fsm_release_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ LOGP(DSAP, LOGL_DEBUG, "Connection release complete\n");
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+}
+
+static void sap_fsm_idle_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+
+ switch ((enum sap_fsm_state) prev_state) {
+ case SAP_STATE_CONNECTING:
+ case SAP_STATE_WAIT_FOR_CARD:
+ /* According to 4.1, if a subscription module is inserted
+ * in the Server and powered on (i.e. STATUS_IND message
+ * indicates "Card reset" state), the Client shall request
+ * the ATR of the subscription module. */
+ if (ms->sap_entity.card_status == SAP_CARD_STATUS_RESET)
+ sap_send_atr_req(ms);
+ break;
+ default:
+ /* Do nothing, suppress compiler warning */
+ break;
+ }
+}
+
+static void sap_fsm_idle_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct msgb *msg = (struct msgb *) data;
+ enum sap_fsm_state state;
+ int rc;
+
+ /* Map event to the corresponding state */
+ switch ((enum sap_msg_type) event) {
+ case SAP_TRANSFER_ATR_REQ:
+ state = SAP_STATE_PROC_ATR_REQ;
+ break;
+ case SAP_TRANSFER_APDU_REQ:
+ state = SAP_STATE_PROC_APDU_REQ;
+ break;
+ case SAP_RESET_SIM_REQ:
+ state = SAP_STATE_PROC_RESET_REQ;
+ break;
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ state = SAP_STATE_PROC_STATUS_REQ;
+ break;
+ case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+ state = SAP_STATE_PROC_SET_TP_REQ;
+ break;
+ case SAP_POWER_SIM_ON_REQ:
+ state = SAP_STATE_PROC_POWERON_REQ;
+ break;
+ case SAP_POWER_SIM_OFF_REQ:
+ state = SAP_STATE_PROC_POWEROFF_REQ;
+ break;
+ default:
+ /* Shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ rc = sap_send_msgb(ms, msg);
+ if (rc)
+ return;
+
+ osmo_fsm_inst_state_chg(fi, state,
+ SAP_FSM_PROC_REQ_TIMEOUT, SAP_FSM_PROC_REQ_T);
+}
+
+static void sap_fsm_response_handler(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct sap_message *sap_msg = (struct sap_message *) data;
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param = NULL;
+ uint16_t param_len = 0;
+ int param_id, rc;
+
+ switch ((enum sap_msg_type) event) {
+ /* Both POWER_SIM_OFF_REQ and RESET_SIM_REQ can be sent in nearly
+ * any state, in order to allow the Client to reactivate
+ * a not accessible subscription module card. */
+ case SAP_POWER_SIM_OFF_REQ:
+ case SAP_RESET_SIM_REQ:
+ OSMO_ASSERT(data != NULL);
+ goto request;
+
+ /* Messages without parameters */
+ case SAP_SET_TRANSPORT_PROTOCOL_RESP:
+ case SAP_POWER_SIM_OFF_RESP:
+ case SAP_POWER_SIM_ON_RESP:
+ case SAP_RESET_SIM_RESP:
+ param_id = -1;
+ break;
+
+ case SAP_TRANSFER_CARD_READER_STATUS_RESP:
+ param_id = SAP_CARD_READER_STATUS;
+ break;
+ case SAP_TRANSFER_APDU_RESP:
+ param_id = SAP_RESPONSE_APDU;
+ break;
+ case SAP_TRANSFER_ATR_RESP:
+ param_id = SAP_ATR;
+ break;
+
+ default:
+ /* Shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ /* We're done with request now */
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+
+ /* Check the ResultCode */
+ rc = sap_check_result_code(sap_msg);
+ if (rc != SAP_RESULT_OK_REQ_PROC_CORR) {
+ LOGP(DSAP, LOGL_NOTICE, "Bad ResultCode: '%s'\n",
+ get_value_string(sap_result_names, rc));
+ goto response;
+ }
+
+ if (param_id < 0)
+ goto response;
+
+ param = sap_get_param(sap_msg, param_id, &param_len);
+ if (!param) {
+ LOGP(DSAP, LOGL_ERROR, "Message '%s' missing "
+ "mandatory parameter '%s'\n",
+ get_value_string(sap_msg_names, sap_msg->msg_id),
+ get_value_string(sap_param_names, param_id));
+ rc = -EINVAL;
+ goto response;
+ }
+
+response:
+ /* Poke optional response handler */
+ if (ms->sap_entity.sap_rsp_cb != NULL) {
+ if (param != NULL) {
+ ms->sap_entity.sap_rsp_cb(ms, rc,
+ sap_msg->msg_id, param_len, param->value);
+ } else {
+ ms->sap_entity.sap_rsp_cb(ms, rc,
+ sap_msg->msg_id, 0, NULL);
+ }
+ }
+
+ return;
+
+request:
+ rc = sap_send_msgb(ms, (struct msgb *) data);
+ if (rc)
+ return;
+
+ osmo_fsm_inst_state_chg(fi, event == SAP_RESET_SIM_REQ ?
+ SAP_STATE_PROC_RESET_REQ : SAP_STATE_PROC_POWEROFF_REQ,
+ SAP_FSM_PROC_REQ_TIMEOUT, SAP_FSM_PROC_REQ_T);
+}
+
+/* Generates mask for a single state or event */
+#define S(x) (1 << x)
+
+/* Figure 4.13: Simplified State Machine */
+static const struct osmo_fsm_state sap_fsm_states[] = {
+ [SAP_STATE_NOT_CONNECTED] = {
+ .name = "NOT_CONNECTED",
+ .out_state_mask = S(SAP_STATE_CONNECTING),
+ .onenter = &sap_fsm_disconnect,
+ },
+ [SAP_STATE_CONNECTING] = {
+ .name = "CONNECTING",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_WAIT_FOR_CARD),
+ .in_event_mask = S(SAP_CONNECT_RESP),
+ .onenter = &sap_fsm_connect,
+ .action = &sap_fsm_conn_handler,
+ },
+ /* NOTE: this is a custom state (i.e. not defined by the specs).
+ * We need it in order to do release procedure correctly. */
+ [SAP_STATE_DISCONNECTING] = {
+ .name = "DISCONNECTING",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED),
+ .in_event_mask = S(SAP_DISCONNECT_RESP),
+ .onenter = &sap_fsm_conn_release,
+ .action = &sap_fsm_release_handler,
+ },
+ /* NOTE: this is a custom state (i.e. not defined by the specs).
+ * We need it in order to wait until SIM card becomes available.
+ * SAP_STATUS_IND event is handled by sap_fsm_allstate_action(). */
+ [SAP_STATE_WAIT_FOR_CARD] = {
+ .name = "WAIT_FOR_CARD",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_IDLE),
+ },
+ [SAP_STATE_IDLE] = {
+ .name = "IDLE",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_PROC_APDU_REQ)
+ | S(SAP_STATE_PROC_ATR_REQ)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_STATUS_REQ)
+ | S(SAP_STATE_PROC_SET_TP_REQ)
+ | S(SAP_STATE_PROC_POWERON_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_ATR_REQ)
+ | S(SAP_TRANSFER_APDU_REQ)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_TRANSFER_CARD_READER_STATUS_REQ)
+ | S(SAP_SET_TRANSPORT_PROTOCOL_REQ)
+ | S(SAP_POWER_SIM_ON_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .onenter = &sap_fsm_idle_enter,
+ .action = &sap_fsm_idle_handler,
+ },
+ [SAP_STATE_PROC_ATR_REQ] = {
+ .name = "PROC_ATR_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_ATR_RESP)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_response_handler,
+ },
+ [SAP_STATE_PROC_APDU_REQ] = {
+ .name = "PROC_APDU_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_APDU_RESP)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_response_handler,
+ },
+ [SAP_STATE_PROC_RESET_REQ] = {
+ .name = "PROC_RESET_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_RESET_SIM_RESP)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_response_handler,
+ },
+ [SAP_STATE_PROC_STATUS_REQ] = {
+ .name = "PROC_STATUS_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_RESET_REQ)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_TRANSFER_CARD_READER_STATUS_RESP)
+ | S(SAP_RESET_SIM_REQ)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_response_handler,
+ },
+ [SAP_STATE_PROC_SET_TP_REQ] = {
+ .name = "PROC_SET_TP_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE),
+ .in_event_mask = S(SAP_SET_TRANSPORT_PROTOCOL_RESP),
+ .action = &sap_fsm_response_handler,
+ },
+ [SAP_STATE_PROC_POWERON_REQ] = {
+ .name = "PROC_POWERON_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE)
+ | S(SAP_STATE_PROC_POWEROFF_REQ),
+ .in_event_mask = S(SAP_POWER_SIM_ON_RESP)
+ | S(SAP_POWER_SIM_OFF_REQ),
+ .action = &sap_fsm_response_handler,
+ },
+ [SAP_STATE_PROC_POWEROFF_REQ] = {
+ .name = "PROC_POWEROFF_REQ",
+ .out_state_mask = S(SAP_STATE_NOT_CONNECTED)
+ | S(SAP_STATE_DISCONNECTING)
+ | S(SAP_STATE_WAIT_FOR_CARD)
+ | S(SAP_STATE_IDLE),
+ .in_event_mask = S(SAP_POWER_SIM_OFF_RESP),
+ .action = &sap_fsm_response_handler,
+ },
+};
+
+static void sap_fsm_tear_down(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+
+ /* Flush buffers, close socket */
+ _sap_close_sock(ms);
+
+ /* Reset SAP state */
+ ms->sap_entity.card_status = SAP_CARD_STATUS_NOT_ACC;
+ ms->sap_entity.max_msg_size = GSM_SAP_LENGTH;
+ ms->sap_entity.fi = NULL;
+}
+
+static void sap_fsm_handle_card_status_ind(struct osmo_fsm_inst *fi,
+ const struct sap_message *sap_msg)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv;
+ struct sap_param *param;
+ uint16_t param_len;
+ uint8_t status;
+
+ param = sap_get_param(sap_msg, SAP_STATUS_CHANGE, &param_len);
+ if (!param || param_len != sizeof(status)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n",
+ get_value_string(sap_param_names, SAP_STATUS_CHANGE));
+ return;
+ }
+
+ status = param->value[0];
+
+ if (ms->sap_entity.card_status != status) {
+ LOGP(DSAP, LOGL_NOTICE, "(SIM) card status change '%s' -> '%s'\n",
+ get_value_string(sap_card_status_names, ms->sap_entity.card_status),
+ get_value_string(sap_card_status_names, status));
+ ms->sap_entity.card_status = status;
+ }
+
+ switch ((enum sap_card_status_type) status) {
+ /* SIM card is ready */
+ case SAP_CARD_STATUS_RESET:
+ if (fi->state != SAP_STATE_IDLE)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ break;
+
+ /* SIM card has recovered after unaccessful state */
+ case SAP_CARD_STATUS_RECOVERED:
+ if (fi->state != SAP_STATE_IDLE)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ break;
+
+ /* SIM card inserted, we need to power it on */
+ case SAP_CARD_STATUS_INSERTED:
+ if (fi->state != SAP_STATE_IDLE)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ sap_send_poweron_req(ms);
+ break;
+
+ case SAP_CARD_STATUS_UNKNOWN_ERROR:
+ case SAP_CARD_STATUS_NOT_ACC:
+ case SAP_CARD_STATUS_REMOVED:
+ default: /* Unknown card status */
+ if (fi->state != SAP_STATE_WAIT_FOR_CARD)
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_WAIT_FOR_CARD, 0, 0);
+ break;
+ }
+}
+
+static void sap_fsm_allstate_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct sap_message *sap_msg = (struct sap_message *) data;
+
+ switch ((enum sap_msg_type) event) {
+ /* Disconnect indication initiated by the Server.
+ * FIXME: at the moment, immediate release is always assumed,
+ * but ideally we should check type of release (using *data) */
+ case SAP_DISCONNECT_IND:
+ /* This message may arrive in any of the sub-states of
+ * the "Connected" state (i.e. connection shall exist) */
+ if (!SAP_STATE_IS_ACTIVE(fi->state))
+ goto not_peritted;
+
+ sap_msg = NULL;
+ /* fall-through */
+
+ /* Disconnect initiated by the Client */
+ case SAP_DISCONNECT_REQ:
+ /* DISCONNECT_REQ has no parameters, so the caller
+ * shall not allocate the message manually. */
+ OSMO_ASSERT(sap_msg == NULL);
+
+ /* If we have no active connection, tear-down immediately */
+ if (!SAP_STATE_IS_ACTIVE(fi->state)) {
+ osmo_fsm_inst_state_chg(fi,
+ SAP_STATE_NOT_CONNECTED, 0, 0);
+ break;
+ }
+
+ /* Trigger Client-initiated connection release */
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_DISCONNECTING,
+ SAP_FSM_CONN_REL_TIMEOUT, SAP_FSM_CONN_REL_T);
+ break;
+
+ /* SIM status indication (inserted or ejected) */
+ case SAP_STATUS_IND:
+ /* This message may arrive in any of the sub-states of
+ * the "Connected" state (i.e. connection shall exist) */
+ if (!SAP_STATE_IS_ACTIVE(fi->state))
+ goto not_peritted;
+
+ sap_fsm_handle_card_status_ind(fi, sap_msg);
+ break;
+
+ case SAP_ERROR_RESP:
+ LOGP(DSAP, LOGL_NOTICE, "RX Error Response from Server\n");
+
+ if (fi->state == SAP_STATE_CONNECTING) {
+ /* Connection establishment error */
+ osmo_fsm_inst_state_chg(fi,
+ SAP_STATE_NOT_CONNECTED, 0, 0);
+ } else if (fi->state > SAP_STATE_IDLE) {
+ /* Error replaces any Request message */
+ osmo_fsm_inst_state_chg(fi,
+ SAP_STATE_IDLE, 0, 0);
+ } else {
+ /* Should not happen in general */
+ goto not_peritted;
+ }
+ break;
+
+ default:
+ /* Shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ return;
+
+not_peritted:
+ LOGPFSML(fi, LOGL_NOTICE, "Event '%s' is not "
+ "permitted in state '%s', please fix!\n",
+ osmo_fsm_event_name(fi->fsm, event),
+ osmo_fsm_state_name(fi->fsm, fi->state));
+}
+
+static int sap_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ switch ((enum sap_fsm_state) fi->state) {
+ /* Connection establishment / release timeout */
+ case SAP_STATE_DISCONNECTING:
+ case SAP_STATE_CONNECTING:
+ LOGP(DSAP, LOGL_NOTICE, "Connection timeout\n");
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ break;
+
+ /* Request processing timeout */
+ case SAP_STATE_PROC_ATR_REQ:
+ case SAP_STATE_PROC_APDU_REQ:
+ case SAP_STATE_PROC_RESET_REQ:
+ case SAP_STATE_PROC_STATUS_REQ:
+ case SAP_STATE_PROC_SET_TP_REQ:
+ case SAP_STATE_PROC_POWERON_REQ:
+ case SAP_STATE_PROC_POWEROFF_REQ:
+ LOGP(DSAP, LOGL_NOTICE, "Timeout waiting for '%s' to complete, "
+ "going back to IDLE\n", osmo_fsm_inst_state_name(fi));
+ osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0);
+ break;
+
+ default:
+ LOGP(DSAP, LOGL_ERROR, "Timeout for unhandled state '%s'\n",
+ osmo_fsm_inst_state_name(fi));
+ }
+
+ /* Do not tear-down FSM */
+ return 0;
+}
+
+static struct osmo_fsm sap_fsm_def = {
+ .name = "sap_fsm",
+ .log_subsys = DSAP,
+ .states = sap_fsm_states,
+ .num_states = ARRAY_SIZE(sap_fsm_states),
+ .event_names = sap_msg_names,
+ .cleanup = &sap_fsm_tear_down,
+ .timer_cb = &sap_fsm_timer_cb,
+ .allstate_action = &sap_fsm_allstate_action,
+ .allstate_event_mask = 0
+ | S(SAP_DISCONNECT_REQ)
+ | S(SAP_DISCONNECT_IND)
+ | S(SAP_STATUS_IND)
+ | S(SAP_ERROR_RESP),
+};
+
+/*! Allocate a new SAP state machine for a given ms.
+ * \param[in] ms MS instance associated with SAP FSM
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_fsm_alloc(struct osmocom_ms *ms)
+{
+ struct osmosap_entity *sap;
+
+ sap = &ms->sap_entity;
+ OSMO_ASSERT(sap->fi == NULL);
+
+ /* Allocate an instance using ms as talloc context */
+ sap->fi = osmo_fsm_inst_alloc(&sap_fsm_def, ms,
+ ms, LOGL_DEBUG, ms->name);
+ if (!sap->fi) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP FSM\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&sap_fsm_def) == 0);
+}
diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c
index 936beb34..0eac8962 100644
--- a/src/host/layer23/src/common/sap_interface.c
+++ b/src/host/layer23/src/common/sap_interface.c
@@ -4,6 +4,7 @@
* (C) 2010,2018 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2011 by Nico Golde <nico@ngolde.de>
+ * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -23,552 +24,325 @@
*
*/
-#include <osmocom/bb/common/osmocom_data.h>
-#include <osmocom/bb/common/logging.h>
-#include <osmocom/bb/common/sap_interface.h>
+#include <unistd.h>
+#include <errno.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/talloc.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/logging.h>
-#include <arpa/inet.h>
+#include <osmocom/bb/common/sap_interface.h>
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/common/sap_fsm.h>
-#define _GNU_SOURCE
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define GSM_SAP_LENGTH 300
-#define GSM_SAP_HEADROOM 32
-
-static void sap_connect(struct osmocom_ms *ms);
-
-static const struct value_string sap_param_names[] = {
- {SAP_MAX_MSG_SIZE, "MaxMsgSize"},
- {SAP_CONNECTION_STATUS, "ConnectionStatus"},
- {SAP_RESULT_CODE, "ResultCode"},
- {SAP_DISCONNECTION_TYPE, "DisconnectionType"},
- {SAP_COMMAND_APDU, "CommandAPDU"},
- {SAP_COMMAND_APDU_7816, "CommandAPDU7816"},
- {SAP_RESPONSE_APDU, "ResponseAPDU"},
- {SAP_ATR, "ATR"},
- {SAP_CARD_READER_STATUS, "CardReaderStatus"},
- {SAP_STATUS_CHANGE, "StatusChange"},
- {SAP_TRANSPORT_PROTOCOL, "TransportProtocol"}
-};
-
-static const struct value_string sap_msg_names[] = {
- {SAP_CONNECT_REQ, "CONNECT_REQ"},
- {SAP_CONNECT_RESP, "CONNECT_RESP"},
- {SAP_DISCONNECT_REQ, "DISCONNECT_REQ"},
- {SAP_DISCONNECT_RESP, "DISCONNECT_RESP"},
- {SAP_DISCONNECT_IND, "DISCONNECT_IND"},
- {SAP_TRANSFER_APDU_REQ, "TRANSFER_APDU_REQ"},
- {SAP_TRANSFER_APDU_RESP, "TRANSFER_APDU_RESP"},
- {SAP_TRANSFER_ATR_REQ, "TRANSFER_ATR_REQ"},
- {SAP_TRANSFER_ATR_RESP, "TRANSFER_ATR_RESP"},
- {SAP_POWER_SIM_OFF_REQ, "POWER_SIM_OFF_REQ"},
- {SAP_POWER_SIM_OFF_RESP, "POWER_SIM_OFF_RESP"},
- {SAP_POWER_SIM_ON_REQ, "POWER_SIM_ON_REQ"},
- {SAP_POWER_SIM_ON_RESP, "POWER_SIM_ON_RESP"},
- {SAP_RESET_SIM_REQ, "RESET_SIM_REQ"},
- {SAP_RESET_SIM_RESP, "RESET_SIM_RESP"},
- {SAP_TRANSFER_CARD_READER_STATUS_REQ, "TRANSFER_CARD_READER_STATUS_REQ"},
- {SAP_TRANSFER_CARD_READER_STATUS_RESP, "TRANSFER_CARD_READER_STATUS_RESP"},
- {SAP_STATUS_IND, "STATUS_IND"},
- {SAP_ERROR_RESP, "ERROR_RESP"},
- {SAP_SET_TRANSPORT_PROTOCOL_REQ, "SET_TRANSPORT_PROTOCOL_REQ"},
- {SAP_SET_TRANSPORT_PROTOCOL_RESP, "SET_TRANSPORT_PROTOCOL_RESP"}
-};
-
-/* BTSAP table 5.18 */
-static const struct value_string sap_result_names[] = {
- {0, "OK, request processed correctly"},
- {1, "Error, no reason defined"},
- {2, "Error, card not accessible"},
- {3, "Error, card (already) powered off"},
- {4, "Error, card removed"},
- {5, "Error, card already powered on"},
- {6, "Error, data not available"},
- {7, "Error, not supported"}
-};
-
-static const struct value_string sap_status_change_names[] = {
- {0, "Unknown Error"},
- {1, "Card reset"},
- {2, "Card not accessible"},
- {3, "Card removed"},
- {4, "Card inserted"},
- {5, "Card recovered"},
-};
-
-static const struct value_string sap_status_names[] = {
- {0, "OK, Server can fulfill requirements"},
- {1, "Error, Server unable to establish connection"},
- {2, "Error, Server does not support maximum message size"},
- {3, "Error, maximum message size by Client is too small"},
- {4, "OK, ongoing call"}
-};
-
-static struct msgb *sap_create_msg(uint8_t id, uint8_t num_params, struct sap_param *params)
+/*! Send ATR request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_atr_req(struct osmocom_ms *ms)
{
struct msgb *msg;
- uint8_t *msgp;
- uint8_t i, plen, padding = 0;
-
- msg = msgb_alloc(GSM_SAP_LENGTH, "osmosap");
- if (!msg) {
- LOGP(DSAP, LOGL_ERROR, "Failed to allocate msg.\n");
- return NULL;
- }
+ int rc;
- /* BTSAP 5.1 */
- msgb_put_u8(msg, id);
- msgb_put_u8(msg, num_params);
- msgb_put_u16(msg, 0);
-
- for(i=0; i<num_params; i++){
- plen = params[i].len;
- msgb_put_u8(msg, params[i].id);
- msgb_put_u8(msg, 0);
- msgb_put_u16(msg, plen);
- if(plen % 4){
- padding = 4 - (plen % 4);
- }
- msgp = msgb_put(msg, plen + padding);
- memcpy(msgp, params[i].value, plen);
-
- if(padding){
- memset(msgp + plen, 0, padding);
- }
+ if (!ms->sap_entity.fi) {
+ LOGP(DSAP, LOGL_ERROR, "SAP interface is not connected\n");
+ return -EAGAIN;
}
- return msg;
-}
-
-static int osmosap_send(struct osmocom_ms *ms, struct msgb *msg)
-{
- if(ms->sap_entity.sap_state == SAP_NOT_CONNECTED)
- sap_connect(ms);
-
- if (ms->sap_wq.bfd.fd <= 0)
- return -EINVAL;
+ msg = sap_msgb_alloc(SAP_TRANSFER_ATR_REQ);
+ if (!msg)
+ return -ENOMEM;
- if (osmo_wqueue_enqueue(&ms->sap_wq, msg) != 0) {
- LOGP(DSAP, LOGL_ERROR, "Failed to enqueue msg.\n");
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_TRANSFER_ATR_REQ, msg);
+ if (rc) {
msgb_free(msg);
- return -1;
+ return rc;
}
return 0;
}
-static int sap_parse_result(struct sap_param *param)
+/*! Send APDU request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \param[in] apdu APDU to be send
+ * \param[in] apdu_len length of APDU
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_apdu(struct osmocom_ms *ms, uint8_t *apdu, uint16_t apdu_len)
{
- if(param->id != SAP_RESULT_CODE){
- LOGP(DSAP, LOGL_INFO, "> Parameter id: %u no valid result type\n", param->id);
- return -1;
- } else {
- LOGP(DSAP, LOGL_INFO, "> RESULT CODE: %s\n",
- get_value_string(sap_result_names, param->value[0]));
- }
+ struct msgb *msg;
+ int rc;
- if(param->value[0] > sizeof(sap_result_names)/sizeof(struct value_string)){
- return -1;
+ if (!ms->sap_entity.fi) {
+ LOGP(DSAP, LOGL_ERROR, "SAP interface is not connected\n");
+ return -EAGAIN;
}
- return 0;
-}
-
-static uint8_t *sap_get_param(uint8_t *data, struct sap_param *param)
-{
- uint8_t *dptr = data;
- uint8_t padlen;
-
- param->id = *dptr++;
- /* skip reserved byte */
- dptr++;
- param->len = *dptr << 8;
- dptr++;
- param->len |= *dptr++;
- param->value = talloc_zero_size(NULL, param->len);
- memcpy(param->value, dptr, param->len);
-
- /* skip parameter and padding and return pointer to next parameter */
- dptr += param->len;
- if(param->len % 4){
- padlen = (4 - param->len % 4);
- } else {
- padlen = 0;
- }
- dptr += padlen;
+ msg = sap_msgb_alloc(SAP_TRANSFER_APDU_REQ);
+ if (!msg)
+ return -ENOMEM;
- return dptr;
-}
+ sap_msgb_add_param(msg, SAP_COMMAND_APDU, apdu_len, apdu);
-static void sap_msg_free(struct sap_msg *msg)
-{
- uint8_t i;
- for(i=0; i<msg->num_params; i++){
- talloc_free(msg->params[i].value);
- talloc_free(msg->params);
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_TRANSFER_APDU_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
}
- talloc_free(msg);
+
+ return 0;
}
-static struct sap_msg *sap_parse_msg(uint8_t *data)
+/*! Send (SIM) reset request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_reset_req(struct osmocom_ms *ms)
{
- struct sap_msg *msg = talloc_zero(NULL, struct sap_msg);
- uint8_t *ptr = data;
- uint8_t i;
+ struct msgb *msg;
+ int rc;
- if(!msg){
- return NULL;
+ if (!ms->sap_entity.fi) {
+ LOGP(DSAP, LOGL_ERROR, "SAP interface is not connected\n");
+ return -EAGAIN;
}
- msg->id = *ptr++;
- LOGP(DSAP, LOGL_INFO, "> %s \n", get_value_string(sap_msg_names, msg->id));
-
- msg->num_params = *ptr++;
- /* skip two reserved null bytes, BTSAP 5.1 */
- ptr += 2;
-
- msg->params = talloc_zero_size(NULL, sizeof(struct sap_param) * msg->num_params);
+ msg = sap_msgb_alloc(SAP_RESET_SIM_REQ);
+ if (!msg)
+ return -ENOMEM;
- for(i=0; i<msg->num_params; i++){
- ptr = sap_get_param(ptr, &msg->params[i]);
- LOGP(DSAP, LOGL_INFO, "> %s %s\n",
- get_value_string(sap_param_names, msg->params[i].id),
- osmo_hexdump(msg->params[i].value, msg->params[i].len));
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_RESET_SIM_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
}
- return msg;
+ return 0;
}
-static void sap_apdu_resp(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
+/*! Send (SIM) power on request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_poweron_req(struct osmocom_ms *ms)
{
struct msgb *msg;
- uint8_t *apdu;
- msg = msgb_alloc(GSM_SAP_LENGTH, "osmosap");
- if(!msg){
- LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory.\n");
- return;
- }
+ int rc;
- apdu = msgb_put(msg, len);
- memcpy(apdu, data, len);
+ if (!ms->sap_entity.fi) {
+ LOGP(DSAP, LOGL_ERROR, "SAP interface is not connected\n");
+ return -EAGAIN;
+ }
- LOGP(DSAP, LOGL_DEBUG, "Forwarding APDU to SIM handler.\n");
- sim_apdu_resp(ms, msg);
-}
+ msg = sap_msgb_alloc(SAP_POWER_SIM_ON_REQ);
+ if (!msg)
+ return -ENOMEM;
-static int sap_adapt_msg_size(struct osmocom_ms *ms, struct sap_param *param)
-{
- uint16_t size;
- size = (param->value[0] << 8) | param->value[1];
- if(size != ms->sap_entity.max_msg_size && size > 0){
- LOGP(DSAP, LOGL_NOTICE, "Server can not handle max_msg_size, adapting.\n");
- ms->sap_entity.max_msg_size = size;
- return -1;
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_POWER_SIM_ON_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
}
+
return 0;
}
-static void sap_atr(struct osmocom_ms *ms)
+/*! Send (SIM) power off request to the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_send_poweroff_req(struct osmocom_ms *ms)
{
struct msgb *msg;
- if(ms->sap_entity.sap_state != SAP_IDLE){
- LOGP(DSAP, LOGL_ERROR, "Attempting to send ATR request while not being idle.\n");
- return;
- }
-
- msg = sap_create_msg(SAP_TRANSFER_ATR_REQ, 0, NULL);
- if(!msg)
- return;
+ int rc;
- osmosap_send(ms, msg);
- ms->sap_entity.sap_state = SAP_PROCESSING_ATR_REQUEST;
-}
-
-static void sap_parse_resp(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
-{
- struct sap_msg *msg = NULL;
- if(len > ms->sap_entity.max_msg_size){
- LOGP(DSAP, LOGL_ERROR, "Read more data than allowed by max_msg_size, ignoring.\n");
- return;
+ if (!ms->sap_entity.fi) {
+ LOGP(DSAP, LOGL_ERROR, "SAP interface is not connected\n");
+ return -EAGAIN;
}
- msg = sap_parse_msg(data);
- if(!msg){
- sap_msg_free(msg);
- return;
- }
+ msg = sap_msgb_alloc(SAP_POWER_SIM_OFF_REQ);
+ if (!msg)
+ return -ENOMEM;
- switch(msg->id){
- case SAP_CONNECT_RESP:
- LOGP(DSAP, LOGL_INFO, "Status: %s\n", get_value_string(sap_status_names, msg->params[0].value[0]));
- if(msg->params[0].value[0] == 0){
- ms->sap_entity.sap_state = SAP_IDLE;
- }
- if(msg->num_params == 2 && msg->params[1].len == 2){
- if(sap_adapt_msg_size(ms, &msg->params[1]) < 0) {
- ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
- } else {
- sap_atr(ms);
- }
- }
- break;
- case SAP_DISCONNECT_RESP:
- ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
- break;
- case SAP_STATUS_IND:
- LOGP(DSAP, LOGL_INFO, "New card state: %s\n", get_value_string(sap_status_change_names,
- msg->params[0].value[0]));
- if(msg->params[0].value[0] != 1){
- /* TODO: handle case in which the card is not ready yet */
- }
- break;
- case SAP_TRANSFER_ATR_RESP:
- if(ms->sap_entity.sap_state != SAP_PROCESSING_ATR_REQUEST){
- LOGP(DSAP, LOGL_ERROR, "got ATR resp in state: %u\n", ms->sap_entity.sap_state);
- return;
- }
- if(msg->num_params >= 2){
- LOGP(DSAP, LOGL_INFO, "ATR: %s\n", osmo_hexdump(msg->params[1].value, msg->params[1].len));
- }
- ms->sap_entity.sap_state = SAP_IDLE;
- break;
- case SAP_TRANSFER_APDU_RESP:
- if(ms->sap_entity.sap_state != SAP_PROCESSING_APDU_REQUEST){
- LOGP(DSAP, LOGL_ERROR, "got APDU resp in state: %u\n", ms->sap_entity.sap_state);
- return;
- }
- if(msg->num_params != 2){
- LOGP(DSAP, LOGL_ERROR, "wrong number of parameters %u in APDU response\n", msg->num_params);
- return;
- }
- ms->sap_entity.sap_state = SAP_IDLE;
- if(sap_parse_result(&msg->params[0]) == 0){
- /* back apdu resp to layer23 */
- sap_apdu_resp(ms, msg->params[1].value, msg->params[1].len);
- LOGP(DSAP, LOGL_INFO, "sap_apdu_resp called, sending data back to layer23\n");
- }
- break;
- case SAP_ERROR_RESP:
- if(ms->sap_entity.sap_state == SAP_CONNECTION_UNDER_NEGOTIATION){
- ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
- } else {
- ms->sap_entity.sap_state = SAP_IDLE;
- }
- break;
- default:
- LOGP(DSAP, LOGL_ERROR, "got unknown or not implemented SAP msgid: %u\n", msg->id);
- break;
+ rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_POWER_SIM_OFF_REQ, msg);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
}
+
+ return 0;
}
-static int sap_read(struct osmo_fd *fd)
+static int sap_read_cb(struct osmo_fd *fd)
{
- struct msgb *msg = NULL;
struct osmocom_ms *ms = (struct osmocom_ms *) fd->data;
- uint8_t *sap_buffer;
+ struct osmosap_entity *sap = &ms->sap_entity;
+ uint8_t buf[GSM_SAP_LENGTH];
+ struct msgb *msg;
ssize_t rc;
- sap_buffer = talloc_zero_size(NULL, ms->sap_entity.max_msg_size);
- if(!sap_buffer){
- fprintf(stderr, "Failed to allocate memory\n");
- return -ENOMEM;
- }
+ /* Prevent buffer overflow */
+ OSMO_ASSERT(sap->max_msg_size <= GSM_SAP_LENGTH);
- rc = read(fd->fd, sap_buffer, ms->sap_entity.max_msg_size - 1);
+ rc = read(fd->fd, buf, sap->max_msg_size);
if (rc < 0) {
- fprintf(stderr, "SAP socket failed\n");
- msgb_free(msg);
- sap_close(ms);
- return rc;
+ LOGP(DSAP, LOGL_ERROR, "SAP socket failed\n");
+ rc = -EIO;
+ goto conn_error;
}
- if(rc == 0) {
- fprintf(stderr, "SAP socket closed by server\n");
- msgb_free(msg);
- sap_close(ms);
- return -ECONNREFUSED;
+ if (rc == 0) {
+ LOGP(DSAP, LOGL_NOTICE, "SAP socket closed by server\n");
+ rc = -ECONNREFUSED;
+ goto conn_error;
}
- sap_buffer[rc] = 0;
- LOGP(DSAP, LOGL_INFO, "Received %zd bytes: %s\n", rc, osmo_hexdump(sap_buffer, rc));
+ LOGP(DSAP, LOGL_DEBUG, "RX SAP message '%s' (len=%zd): %s\n",
+ get_value_string(sap_msg_names, buf[0]),
+ rc, osmo_hexdump(buf, rc));
- sap_parse_resp(ms, sap_buffer, rc);
-
- talloc_free(sap_buffer);
+ /* Parse received SAP message and allocate a new msgb */
+ msg = sap_msg_parse(buf, rc, sap->max_msg_size);
+ if (!msg) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to parse SAP message\n");
+ return -EINVAL;
+ }
- if (ms->sap_entity.msg_handler){
- ms->sap_entity.msg_handler(msg, ms);
+ /* Pass parsed message to our FSM using message ID as event */
+ rc = osmo_fsm_inst_dispatch(sap->fi, msg->data[0], msg->data);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
}
+
+ /* Pass to (optional) SAP message handler */
+ if (sap->sap_msg_cb)
+ sap->sap_msg_cb(ms, msg);
+ else
+ msgb_free(msg);
+
return 0;
+
+conn_error:
+ /* Immediately tear-down FSM */
+ osmo_fsm_inst_state_chg(sap->fi, SAP_STATE_NOT_CONNECTED, 0, 0);
+ return rc;
}
-static int sap_write(struct osmo_fd *fd, struct msgb *msg)
+static int sap_write_cb(struct osmo_fd *fd, struct msgb *msg)
{
ssize_t rc;
if (fd->fd <= 0)
return -EINVAL;
- LOGP(DSAP, LOGL_INFO, "< %s\n", osmo_hexdump(msg->data, msg->len));
rc = write(fd->fd, msg->data, msg->len);
if (rc != msg->len) {
- LOGP(DSAP, LOGL_ERROR, "Failed to write data: rc: %zd\n", rc);
+ LOGP(DSAP, LOGL_ERROR, "Failed to write data\n");
return rc;
}
- return 0;
-}
-
-static void sap_connect(struct osmocom_ms *ms)
-{
- uint8_t buffer[3];
- struct msgb *msg;
- uint16_t size = ms->sap_entity.max_msg_size;
- struct sap_param params[1];
-
- params[0].id = SAP_MAX_MSG_SIZE;
- params[0].len = 2;
-
- if(ms->sap_entity.sap_state != SAP_NOT_CONNECTED) {
- LOGP(DSAP, LOGL_ERROR, "Attempting to connect while there is an active connection.\n");
- return;
- }
-
- buffer[0] = (size >> 8) & 0xFF;
- buffer[1] = size & 0xFF;
- buffer[2] = 0;
- params[0].value = buffer;
-
- msg = sap_create_msg(SAP_CONNECT_REQ, 1, params);
- if(!msg)
- return;
-
- osmosap_send(ms, msg);
+ LOGP(DSAP, LOGL_DEBUG, "TX SAP message '%s' (len=%u): %s\n",
+ get_value_string(sap_msg_names, msg->data[0]),
+ msg->len, osmo_hexdump(msg->data, msg->len));
- ms->sap_entity.sap_state = SAP_CONNECTION_UNDER_NEGOTIATION;
-}
-
-static void sap_disconnect(struct osmocom_ms *ms)
-{
- struct msgb *msg;
- if(ms->sap_entity.sap_state != SAP_NOT_CONNECTED && ms->sap_entity.sap_state != SAP_CONNECTION_UNDER_NEGOTIATION){
- LOGP(DSAP, LOGL_ERROR, "Attempting to disconnect while no active connection.\n");
- return;
- }
-
- msg = sap_create_msg(SAP_DISCONNECT_REQ, 0, NULL);
- if(!msg)
- return;
-
- osmosap_send(ms, msg);
-
- ms->sap_entity.sap_state = SAP_NOT_CONNECTED;
+ return 0;
}
-static void sap_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t len)
+/*! Establishes SAP connection to the Server,
+ * allocates SAP FSM, and triggers connection procedure.
+ * \param[in] ms MS instance with configured SAP socket path
+ * \returns 0 in case of success, negative in case of error
+ */
+int sap_open(struct osmocom_ms *ms)
{
- struct msgb *msg;
- struct sap_param params[1];
-
- params[0].id = SAP_COMMAND_APDU;
- params[0].len = len;
- params[0].value = data;
-
- if(ms->sap_entity.sap_state != SAP_IDLE){
- LOGP(DSAP, LOGL_ERROR, "Attempting to send APDU request while not being idle.\n");
- return;
- }
+ int rc;
- msg = sap_create_msg(SAP_TRANSFER_APDU_REQ, 1, params);
- if(!msg)
- return;
+ LOGP(DSAP, LOGL_INFO, "Establishing SAP connection "
+ "(using socket '%s')\n", ms->settings.sap_socket_path);
- osmosap_send(ms, msg);
-
- ms->sap_entity.sap_state = SAP_PROCESSING_APDU_REQUEST;
-}
-
-int sap_open(struct osmocom_ms *ms, const char *socket_path)
-{
- ssize_t rc;
-
- rc = osmo_sock_unix_init_ofd(&ms->sap_wq.bfd, SOCK_STREAM, 0, socket_path, OSMO_SOCK_F_CONNECT);
+ rc = osmo_sock_unix_init_ofd(&ms->sap_wq.bfd, SOCK_STREAM, 0,
+ ms->settings.sap_socket_path, OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGP(DSAP, LOGL_ERROR, "Failed to create unix domain socket %s: %s\n",
- socket_path, strerror(-rc));
- ms->sap_entity.sap_state = SAP_SOCKET_ERROR;
+ ms->settings.sap_socket_path, strerror(-rc));
return rc;
}
osmo_wqueue_init(&ms->sap_wq, 100);
ms->sap_wq.bfd.data = ms;
- ms->sap_wq.read_cb = sap_read;
- ms->sap_wq.write_cb = sap_write;
+ ms->sap_wq.read_cb = &sap_read_cb;
+ ms->sap_wq.write_cb = &sap_write_cb;
- sap_connect(ms);
+ /* Allocate a SAP FSM for a given ms */
+ rc = sap_fsm_alloc(ms);
+ if (rc) {
+ _sap_close_sock(ms);
+ return rc;
+ }
- return 0;
+ /* Initiate SAP connection with Server */
+ LOGP(DSAP, LOGL_DEBUG, "Connecting to the Server...\n");
+ return osmo_fsm_inst_state_chg(ms->sap_entity.fi, SAP_STATE_CONNECTING,
+ SAP_FSM_CONN_EST_TIMEOUT, SAP_FSM_CONN_EST_T);
}
+/*! Closes SAP connection with the Server.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
int sap_close(struct osmocom_ms *ms)
{
- if (ms->sap_wq.bfd.fd <= 0)
+ if (ms->sap_entity.fi == NULL) {
+ LOGP(DSAP, LOGL_NOTICE, "No active SAP connection (no FSM)\n");
return -EINVAL;
+ }
- sap_disconnect(ms);
- 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;
-}
-
-/* same signature as in L1CTL, so it can be called from sim.c */
-int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
-{
- //LOGP(DSAP, LOGL_ERROR, "Received the following APDU from sim.c: %s\n" ,
- // osmo_hexdump(data, length));
- sap_apdu(ms, data, length);
-
- return 0;
+ LOGP(DSAP, LOGL_INFO, "Closing SAP connection\n");
+ return osmo_fsm_inst_dispatch(ms->sap_entity.fi,
+ SAP_DISCONNECT_REQ, NULL);
}
-/* register message handler for messages that are sent from L2->L3 */
-int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb)
+/*! Low-level function for closing SAP (socket) connection.
+ * \param[in] ms MS instance with active SAP connection
+ * \returns 0 in case of success, negative in case of error
+ */
+int _sap_close_sock(struct osmocom_ms *ms)
{
- ms->sap_entity.msg_handler = cb;
+ if (ms->sap_wq.bfd.fd <= 0)
+ return -EINVAL;
- return 0;
-}
+ close(ms->sap_wq.bfd.fd);
+ ms->sap_wq.bfd.fd = -1;
-int osmosap_sapsocket(struct osmocom_ms *ms, const char *path)
-{
- struct gsm_settings *set = &ms->settings;
- memset(set->sap_socket_path, 0, sizeof(set->sap_socket_path));
- osmo_strlcpy(set->sap_socket_path, path, sizeof(set->sap_socket_path) - 1);
+ osmo_fd_unregister(&ms->sap_wq.bfd);
+ osmo_wqueue_clear(&ms->sap_wq);
return 0;
}
-/* init */
-int osmosap_init(struct osmocom_ms *ms)
+/*! Init SAP client state for a given MS. */
+void sap_init(struct osmocom_ms *ms)
{
struct osmosap_entity *sap = &ms->sap_entity;
LOGP(DSAP, LOGL_INFO, "init SAP client\n");
- sap->sap_state = SAP_NOT_CONNECTED;
- sap->max_msg_size = GSM_SAP_LENGTH;
- return 0;
+ /* Default MaxMsgSize (to be negotiated) */
+ sap->max_msg_size = GSM_SAP_LENGTH;
+ /* SIM card status is not known yet */
+ sap->card_status = SAP_CARD_STATUS_NOT_ACC;
}
-
diff --git a/src/host/layer23/src/common/sap_proto.c b/src/host/layer23/src/common/sap_proto.c
new file mode 100644
index 00000000..ecacfe05
--- /dev/null
+++ b/src/host/layer23/src/common/sap_proto.c
@@ -0,0 +1,322 @@
+/*
+ * SAP (SIM Access Profile) protocol definition
+ * based on Bluetooth SAP specification
+ *
+ * (C) 2011 by Nico Golde <nico@ngolde.de>
+ * (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 <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/bb/common/sap_proto.h>
+#include <osmocom/bb/common/logging.h>
+
+/* Table 5.1: Message Overview */
+const struct value_string sap_msg_names[] = {
+ { SAP_CONNECT_REQ, "CONNECT_REQ" },
+ { SAP_CONNECT_RESP, "CONNECT_RESP" },
+ { SAP_DISCONNECT_REQ, "DISCONNECT_REQ" },
+ { SAP_DISCONNECT_RESP, "DISCONNECT_RESP" },
+ { SAP_DISCONNECT_IND, "DISCONNECT_IND" },
+ { SAP_TRANSFER_APDU_REQ, "TRANSFER_APDU_REQ" },
+ { SAP_TRANSFER_APDU_RESP, "TRANSFER_APDU_RESP" },
+ { SAP_TRANSFER_ATR_REQ, "TRANSFER_ATR_REQ" },
+ { SAP_TRANSFER_ATR_RESP, "TRANSFER_ATR_RESP" },
+ { SAP_POWER_SIM_OFF_REQ, "POWER_SIM_OFF_REQ" },
+ { SAP_POWER_SIM_OFF_RESP, "POWER_SIM_OFF_RESP" },
+ { SAP_POWER_SIM_ON_REQ, "POWER_SIM_ON_REQ" },
+ { SAP_POWER_SIM_ON_RESP, "POWER_SIM_ON_RESP" },
+ { SAP_RESET_SIM_REQ, "RESET_SIM_REQ" },
+ { SAP_RESET_SIM_RESP, "RESET_SIM_RESP" },
+ { SAP_TRANSFER_CARD_READER_STATUS_REQ, "TRANSFER_CARD_READER_STATUS_REQ" },
+ { SAP_TRANSFER_CARD_READER_STATUS_RESP, "TRANSFER_CARD_READER_STATUS_RESP" },
+ { SAP_STATUS_IND, "STATUS_IND" },
+ { SAP_ERROR_RESP, "ERROR_RESP" },
+ { SAP_SET_TRANSPORT_PROTOCOL_REQ, "SET_TRANSPORT_PROTOCOL_REQ" },
+ { SAP_SET_TRANSPORT_PROTOCOL_RESP, "SET_TRANSPORT_PROTOCOL_RESP" },
+ { 0, NULL }
+};
+
+/* Table 5.15: List of Parameter IDs */
+const struct value_string sap_param_names[] = {
+ { SAP_MAX_MSG_SIZE, "MaxMsgSize" },
+ { SAP_CONNECTION_STATUS, "ConnectionStatus" },
+ { SAP_RESULT_CODE, "ResultCode" },
+ { SAP_DISCONNECTION_TYPE, "DisconnectionType" },
+ { SAP_COMMAND_APDU, "CommandAPDU" },
+ { SAP_COMMAND_APDU_7816, "CommandAPDU7816" },
+ { SAP_RESPONSE_APDU, "ResponseAPDU" },
+ { SAP_ATR, "ATR" },
+ { SAP_CARD_READER_STATUS, "CardReaderStatus" },
+ { SAP_STATUS_CHANGE, "StatusChange" },
+ { SAP_TRANSPORT_PROTOCOL, "TransportProtocol" },
+ { 0, NULL }
+};
+
+/* Table 5.18: Possible values for ResultCode */
+const struct value_string sap_result_names[] = {
+ { SAP_RESULT_OK_REQ_PROC_CORR, "OK, request processed correctly" },
+ { SAP_RESULT_ERROR_NO_REASON, "Error, no reason defined" },
+ { SAP_RESULT_ERROR_CARD_NOT_ACC, "Error, card not accessible" },
+ { SAP_RESULT_ERROR_CARD_POWERED_OFF, "Error, card (already) powered off" },
+ { SAP_RESULT_ERROR_CARD_REMOVED, "Error, card removed" },
+ { SAP_RESULT_ERROR_CARD_POWERED_ON, "Error, card already powered on" },
+ { SAP_RESULT_ERROR_DATA_UNAVAIL, "Error, data not available" },
+ { SAP_RESULT_ERROR_NOT_SUPPORTED, "Error, not supported "},
+ { 0, NULL }
+};
+
+/* Table 5.19: Possible values for StatusChange */
+const struct value_string sap_card_status_names[] = {
+ { SAP_CARD_STATUS_UNKNOWN_ERROR, "Unknown Error" },
+ { SAP_CARD_STATUS_RESET, "Card reset" },
+ { SAP_CARD_STATUS_NOT_ACC, "Card not accessible" },
+ { SAP_CARD_STATUS_REMOVED, "Card removed" },
+ { SAP_CARD_STATUS_INSERTED, "Card inserted" },
+ { SAP_CARD_STATUS_RECOVERED, "Card recovered" },
+ { 0, NULL }
+};
+
+/* Table 5.16: Possible values for ConnectionStatus */
+const struct value_string sap_conn_status_names[] = {
+ { SAP_CONN_STATUS_OK_READY, "OK, Server can fulfill requirements" },
+ { SAP_CONN_STATUS_ERROR_CONN, "Error, Server unable to establish connection" },
+ { SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE, "Error, Server does not support maximum message size" },
+ { SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE, "Error, maximum message size by Client is too small" },
+ { SAP_CONN_STATUS_OK_CALL, "OK, ongoing call" },
+ { 0, NULL }
+};
+
+/*! Allocate a new message buffer with SAP message header.
+ * \param[in] msg_id SAP message identifier
+ * \returns message buffer in case of success, NULL otherwise
+ */
+struct msgb *sap_msgb_alloc(uint8_t msg_id)
+{
+ struct sap_message *sap_msg;
+ struct msgb *msg;
+
+ msg = msgb_alloc(GSM_SAP_LENGTH, "sap_msg");
+ if (!msg) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP message\n");
+ return NULL;
+ }
+
+ sap_msg = (struct sap_message *) msgb_put(msg, sizeof(*sap_msg));
+ sap_msg->msg_id = msg_id;
+
+ return msg;
+}
+
+/*! Add a new parameter to a given SAP message buffer.
+ * Padding is added automatically, SAP message header
+ * (number of parameters) is also updated automatically.
+ * \param[in] msg SAP message buffer
+ * \param[in] param_type parameter type (see sap_param_type enum)
+ * \param[in] param_len parameter length
+ * \param[in] param_value pointer to parameter value
+ */
+void sap_msgb_add_param(struct msgb *msg,
+ enum sap_param_type param_type,
+ uint16_t param_len, const uint8_t *param_value)
+{
+ struct sap_message *sap_msg;
+ struct sap_param *param;
+ uint8_t padding;
+ uint8_t *buf;
+
+ /* Update number of parameters */
+ sap_msg = (struct sap_message *) msg->data;
+ sap_msg->num_params++;
+
+ /* Allocate a new parameter */
+ param = (struct sap_param *) msgb_put(msg, sizeof(*param));
+ param->param_id = param_type;
+ param->reserved[0] = 0x00;
+
+ /* Encode parameter value and length */
+ param->length = htons(param_len);
+ buf = msgb_put(msg, param_len);
+ memcpy(buf, param_value, param_len);
+
+ /* Optional padding */
+ padding = 4 - (param_len % 4);
+ if (padding) {
+ buf = msgb_put(msg, padding);
+ memset(buf, 0x00, padding);
+ }
+}
+
+/*! Attempt to find a given parameter in a given SAP message.
+ * \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 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)
+{
+ const uint8_t *ptr = sap_msg->payload;
+ struct sap_param *param = NULL;
+ uint16_t plen;
+ int i;
+
+ /* We assume that message is parsed already,
+ * so we don't check for buffer overflows */
+ for (i = 0; i < sap_msg->num_params; i++) {
+ /* Parse one parameter */
+ param = (struct sap_param *) ptr;
+ plen = ntohs(param->length);
+
+ /* Match against a given ID */
+ if (param->param_id == param_type) {
+ if (param_len != NULL)
+ *param_len = plen;
+ return param;
+ }
+
+ /* Shift pointer to the next parameter */
+ ptr += sizeof(*param) + plen;
+ /* Optional padding */
+ ptr += 4 - (plen % 4);
+ }
+
+ return NULL;
+}
+
+/*! Parse SAP message from a given buffer into a new message buffer.
+ * \param[in] buf pointer to a buffer with to be parsed message
+ * \param[in] buf_len length of the buffer
+ * \param[in] max_msg_size max (negotiated) message size
+ * \returns new message buffer with parsed message, NULL otherwise
+ */
+struct msgb *sap_msg_parse(const uint8_t *buf, size_t buf_len, int max_msg_size)
+{
+ const struct sap_message *sap_msg;
+ const uint8_t *ptr;
+ struct msgb *msg;
+ size_t len;
+ int i;
+
+ /* Message header is mandatory */
+ if (buf_len < sizeof(*sap_msg)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing SAP message header\n");
+ return NULL;
+ }
+
+ /* MaxMsgSize limitation (optional) */
+ if (max_msg_size > 0 && buf_len > max_msg_size) {
+ LOGP(DSAP, LOGL_ERROR, "Buffer (len=%zu) is bigger than "
+ "given MaxMsgSize=%d\n", buf_len, max_msg_size);
+ return NULL;
+ }
+
+ sap_msg = (const struct sap_message *) buf;
+ len = buf_len - sizeof(*sap_msg);
+ ptr = sap_msg->payload;
+
+ LOGP(DSAP, LOGL_DEBUG, "SAP message '%s' has %u parameter(s)\n",
+ get_value_string(sap_msg_names, sap_msg->msg_id),
+ sap_msg->num_params);
+
+ for (i = 0; i < sap_msg->num_params; i++) {
+ struct sap_param *param;
+ uint16_t param_len;
+ uint16_t offset;
+
+ /* Prevent buffer overflow */
+ if (len < sizeof(*param))
+ goto malformed;
+
+ /* Parse one parameter */
+ param = (struct sap_param *) ptr;
+ param_len = ntohs(param->length);
+
+ LOGP(DSAP, LOGL_DEBUG, "SAP parameter '%s' (len=%u): %s\n",
+ get_value_string(sap_param_names, param->param_id),
+ param_len, osmo_hexdump(param->value, param_len));
+
+ /* Calculate relative offset */
+ offset = sizeof(*param) + param_len;
+ offset += 4 - (param_len % 4); /* Optional padding */
+
+ /* Prevent buffer overflow */
+ if (offset > len)
+ goto malformed;
+
+ len -= offset;
+ ptr += offset;
+ }
+
+ /* Allocate a new message buffer */
+ msg = msgb_alloc(GSM_SAP_LENGTH, "sap_msg");
+ if (!msg) {
+ LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP message\n");
+ return NULL;
+ }
+
+ msg->data = msgb_put(msg, buf_len);
+ memcpy(msg->data, buf, buf_len);
+
+ return msg;
+
+malformed:
+ LOGP(DSAP, LOGL_ERROR, "Malformed SAP message "
+ "(parameter %i/%u)\n", i + 1, sap_msg->num_params);
+ return NULL;
+}
+
+/*! Parse ResultCode from a given SAP message.
+ * \param[in] sap_msg pointer to SAP message header
+ * \returns parsed ResultCode (if found), negative otherwise
+ */
+int sap_check_result_code(const struct sap_message *sap_msg)
+{
+ struct sap_param *param;
+ uint16_t param_len;
+ uint8_t res_code;
+
+ param = sap_get_param(sap_msg, SAP_RESULT_CODE, &param_len);
+ if (!param || param_len != sizeof(res_code)) {
+ LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n",
+ get_value_string(sap_param_names, SAP_RESULT_CODE));
+ return -EINVAL;
+ }
+
+ res_code = param->value[0];
+ if (res_code >= ARRAY_SIZE(sap_result_names)) {
+ LOGP(DSAP, LOGL_ERROR, "Unknown SAP ResultCode=0x%02x\n", res_code);
+ return -EINVAL;
+ }
+
+ LOGP(DSAP, LOGL_DEBUG, "SAP ResultCode is '%s'\n",
+ get_value_string(sap_result_names, res_code));
+
+ return res_code;
+}
diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c
index 7f5240dd..1e2bc513 100644
--- a/src/host/layer23/src/common/sim.c
+++ b/src/host/layer23/src/common/sim.c
@@ -146,8 +146,6 @@ void gsm_sim_reply(struct osmocom_ms *ms, uint8_t result_type, uint8_t *result,
struct gsm_sim *sim = &ms->sim;
struct msgb *msg = sim->job_msg;
struct sim_hdr *sh;
- uint8_t *payload;
- uint16_t payload_len;
struct gsm_sim_handler *handler;
LOGP(DSIM, LOGL_INFO, "sending result to callback function "
@@ -165,12 +163,8 @@ void gsm_sim_reply(struct osmocom_ms *ms, uint8_t result_type, uint8_t *result,
return;
}
- payload = msg->data + sizeof(*sh);
- payload_len = msg->len - sizeof(*sh);
-
/* remove data */
- msg->tail -= payload_len;
- msg->len -= payload_len;
+ msgb_get(msg, msg->len - sizeof(*sh));
/* add reply data */
sh->job_type = result_type;
@@ -186,6 +180,8 @@ void gsm_sim_reply(struct osmocom_ms *ms, uint8_t result_type, uint8_t *result,
/* send APDU to card reader */
static int sim_apdu_send(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
{
+ int rc;
+
LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n",
data[0], data[1]);
@@ -203,13 +199,13 @@ static int sim_apdu_send(struct osmocom_ms *ms, uint8_t *data, uint16_t length)
* it makes more sense to do it here then in L1CTL */
if (ms->subscr.sim_type == GSM_SIM_TYPE_SAP) {
LOGP(DSIM, LOGL_INFO, "Using SAP backend\n");
- osmosap_send_apdu(ms, data, length);
+ rc = sap_send_apdu(ms, data, length);
} else {
LOGP(DSIM, LOGL_INFO, "Using built-in SIM reader\n");
- l1ctl_tx_sim_req(ms, data, length);
+ rc = l1ctl_tx_sim_req(ms, data, length);
}
- return 0;
+ return rc;
}
/* dequeue messages (RSL-SAP) */
@@ -946,6 +942,7 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg)
pin_cause[0] = SIM_CAUSE_PUC_BLOCKED;
pin_cause[1] = 0;
}
+ break;
case SIM_JST_PIN1_UNLOCK:
case SIM_JST_PIN1_CHANGE:
case SIM_JST_PIN1_DISABLE:
@@ -1135,7 +1132,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/common/sysinfo.c b/src/host/layer23/src/common/sysinfo.c
index b42bd653..f927773f 100644
--- a/src/host/layer23/src/common/sysinfo.c
+++ b/src/host/layer23/src/common/sysinfo.c
@@ -78,7 +78,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
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)
diff --git a/src/host/layer23/src/misc/Makefile.am b/src/host/layer23/src/misc/Makefile.am
index 9c2bc4d4..78ab962f 100644
--- a/src/host/layer23/src/misc/Makefile.am
+++ b/src/host/layer23/src/misc/Makefile.am
@@ -4,10 +4,11 @@ LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOC
bin_PROGRAMS = bcch_scan ccch_scan echo_test cell_log cbch_sniff
+noinst_HEADERS = bcch_scan.h geo.h
+
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 \
- ../../../gsmmap/geo.c
+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
diff --git a/src/host/layer23/src/misc/app_bcch_scan.c b/src/host/layer23/src/misc/app_bcch_scan.c
index 7b21ed79..59466991 100644
--- a/src/host/layer23/src/misc/app_bcch_scan.c
+++ b/src/host/layer23/src/misc/app_bcch_scan.c
@@ -33,6 +33,7 @@
#include <osmocom/core/signal.h>
#include <l1ctl_proto.h>
+#include "bcch_scan.h"
static int signal_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
@@ -52,8 +53,8 @@ static int signal_cb(unsigned int subsys, unsigned int signal,
int l23_app_init(struct osmocom_ms *ms)
{
- /* don't do layer3_init() as we don't want an actualy L3 */
- fps_init(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);
return osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL);
}
diff --git a/src/host/layer23/src/misc/app_cbch_sniff.c b/src/host/layer23/src/misc/app_cbch_sniff.c
index 8256eaf6..ed85cefe 100644
--- a/src/host/layer23/src/misc/app_cbch_sniff.c
+++ b/src/host/layer23/src/misc/app_cbch_sniff.c
@@ -34,6 +34,8 @@
#include <osmocom/core/signal.h>
#include <osmocom/gsm/rsl.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
#include <l1ctl_proto.h>
struct osmocom_ms *g_ms;
@@ -41,6 +43,8 @@ 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,
+ chan_nr, s->tsc,
GSM48_CMODE_SIGN, 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);
}
}
@@ -179,7 +190,7 @@ static int signal_cb(unsigned int subsys, unsigned int signal,
int l23_app_init(struct osmocom_ms *ms)
{
- /* don't do layer3_init() as we don't want an actualy L3 */
+ /* don't do layer3_init() as we don't want an actual L3 */
g_ms = ms;
lapdm_channel_set_l3(&ms->lapdm_channel, &rcv_rsl, ms);
diff --git a/src/host/layer23/src/misc/app_ccch_scan.c b/src/host/layer23/src/misc/app_ccch_scan.c
index 88a2bef3..be2d9aea 100644
--- a/src/host/layer23/src/misc/app_ccch_scan.c
+++ b/src/host/layer23/src/misc/app_ccch_scan.c
@@ -166,7 +166,7 @@ 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;
diff --git a/src/host/layer23/src/misc/bcch_scan.c b/src/host/layer23/src/misc/bcch_scan.c
index 3ba3a1cd..5dc0bc3b 100644
--- a/src/host/layer23/src/misc/bcch_scan.c
+++ b/src/host/layer23/src/misc/bcch_scan.c
@@ -199,9 +199,13 @@ static void cinfo_timer_cb(void *data)
case BSCAN_S_WAIT_DATA:
cinfo_next_cell(data);
break;
+ case BSCAN_S_NONE:
+ case BSCAN_S_DONE:
+ break;
}
}
+#if 0
/* Update cell_info for current cell with received BCCH info */
static int rx_bcch_info(const uint8_t *data)
{
@@ -233,12 +237,13 @@ static int rx_bcch_info(const uint8_t *data)
static int rx_sch_info()
{
/* FIXME */
+ return 0;
}
+#endif
static int bscan_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
- struct cell_info *ci = fps.cur_cell;
struct osmocom_ms *ms;
struct osmobb_meas_res *mr;
uint16_t arfcn;
@@ -279,6 +284,9 @@ static int bscan_sig_cb(unsigned int subsys, unsigned int signal,
}
_cinfo_start_arfcn(rc);
break;
+ case FPS_S_NONE:
+ case FPS_S_BINFO:
+ break;
}
break;
case S_L1CTL_FBSB_RESP:
diff --git a/src/host/layer23/src/misc/bcch_scan.h b/src/host/layer23/src/misc/bcch_scan.h
new file mode 100644
index 00000000..ab861b42
--- /dev/null
+++ b/src/host/layer23/src/misc/bcch_scan.h
@@ -0,0 +1,6 @@
+#pragma once
+
+struct osmocom_ms;
+
+int fps_start(struct osmocom_ms *ms);
+int fps_init(void);
diff --git a/src/host/layer23/src/misc/cell_log.c b/src/host/layer23/src/misc/cell_log.c
index 7340dcb9..9edd742e 100644
--- a/src/host/layer23/src/misc/cell_log.c
+++ b/src/host/layer23/src/misc/cell_log.c
@@ -438,7 +438,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;
diff --git a/src/host/layer23/src/misc/geo.c b/src/host/layer23/src/misc/geo.c
new file mode 100644
index 00000000..65633d2c
--- /dev/null
+++ b/src/host/layer23/src/misc/geo.c
@@ -0,0 +1,47 @@
+#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/layer23/src/misc/geo.h b/src/host/layer23/src/misc/geo.h
new file mode 100644
index 00000000..25e26cba
--- /dev/null
+++ b/src/host/layer23/src/misc/geo.h
@@ -0,0 +1,12 @@
+/* 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/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am
index 4e80e4ea..783ae162 100644
--- a/src/host/layer23/src/mobile/Makefile.am
+++ b/src/host/layer23/src/mobile/Makefile.am
@@ -4,7 +4,7 @@ LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOG
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 \
+ gsm48_rr.c gsm414.c mnccms.c settings.c subscriber.c support.c \
transaction.c vty_interface.c voice.c mncc_sock.c primitives.c
bin_PROGRAMS = mobile
diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c
index f53445a6..a5feb796 100644
--- a/src/host/layer23/src/mobile/app_mobile.c
+++ b/src/host/layer23/src/mobile/app_mobile.c
@@ -39,6 +39,8 @@
#include <osmocom/bb/mobile/voice.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/telnet_interface.h>
@@ -98,7 +100,7 @@ int mobile_signal_cb(unsigned int subsys, unsigned int signal,
/* 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,7 +110,7 @@ int mobile_signal_cb(unsigned int subsys, unsigned int signal,
/* insert test card, if enabled */
switch (set->sim_type) {
- case GSM_SIM_TYPE_READER:
+ case GSM_SIM_TYPE_L1PHY:
/* trigger sim card reader process */
gsm_subscr_simcard(ms);
break;
@@ -198,7 +200,10 @@ static int mobile_init(struct osmocom_ms *ms)
lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms);
/* init SAP client before SIM card starts up */
- osmosap_init(ms);
+ 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);
@@ -219,16 +224,6 @@ static int mobile_init(struct osmocom_ms *ms)
return rc;
}
-#if 0
- rc = sap_open(ms, ms->settings.sap_socket_path);
- if (rc < 0) {
- fprintf(stderr, "Failed during sap_open(), no SIM reader\n");
- ms->sap_wq.bfd.fd = -1;
- mobile_exit(ms, 1);
- return rc;
- }
-#endif
-
gsm_random_imei(&ms->settings);
mobile_set_shutdown(ms, MS_SHUTDOWN_NONE);
@@ -434,7 +429,7 @@ static struct vty_app_info vty_info = {
/* global init */
int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
- const char *config_file, const char *vty_ip, uint16_t vty_port)
+ const char *config_file)
{
struct telnet_connection dummy_conn;
int rc = 0;
@@ -445,31 +440,29 @@ int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
vty_info.tall_ctx = l23_ctx;
vty_init(&vty_info);
- logging_vty_add_cmds(NULL);
+ 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) {
- fprintf(stderr, "Failed to parse the config file:"
- " '%s'\n", config_file);
- fprintf(stderr, "Please check or create config file"
- " using: 'touch %s'\n", config_file);
- fprintf(stderr, "or use one from "
- "'doc/examples/mobile/'\n");
+ 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;
}
- printf("Using configuration from %s\n", config_file);
+ LOGP(DMOB, LOGL_INFO, "Using configuration from '%s'\n", config_file);
}
vty_reading = 0;
- rc = telnet_init_dynif(l23_ctx, NULL, vty_ip, vty_port);
+ rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB);
if (rc < 0) {
- fprintf(stderr, "Cannot init VTY on %s port %u: %s\n",
- vty_ip, vty_port, strerror(errno));
+ 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;
}
- printf("VTY available on %s %u\n", vty_ip, vty_port);
osmo_signal_register_handler(SS_GLOBAL, &global_signal_cb, NULL);
osmo_signal_register_handler(SS_L1CTL, &mobile_signal_cb, NULL);
@@ -478,7 +471,7 @@ int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *),
if (llist_empty(&ms_list)) {
struct osmocom_ms *ms;
- printf("No Mobile Station defined, creating: MS '1'\n");
+ LOGP(DMOB, LOGL_NOTICE, "No Mobile Station defined, creating: MS '1'\n");
ms = mobile_new("1");
if (!ms)
return -1;
diff --git a/src/host/layer23/src/mobile/gsm322.c b/src/host/layer23/src/mobile/gsm322.c
index f2b51df0..cc4f0cd0 100644
--- a/src/host/layer23/src/mobile/gsm322.c
+++ b/src/host/layer23/src/mobile/gsm322.c
@@ -156,7 +156,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 +172,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 +201,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 +427,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
@@ -975,7 +975,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) {
@@ -1047,7 +1047,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);
@@ -1074,7 +1074,7 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
/* 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. "
@@ -1555,7 +1555,7 @@ static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
/* 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.
@@ -1847,7 +1847,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,14 +1860,14 @@ 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
- band = gsm_arfcn2band(index2arfcn(i));
+ gsm_arfcn2band_rc(index2arfcn(i), &band);
class = class_of_band(ms, band);
c1 = calculate_c1(DCS, rxlev2dbm(cs->list[i].rxlev),
s->rxlev_acc_min_db,
@@ -2195,7 +2195,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 +2249,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. */
@@ -2462,7 +2462,7 @@ 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;
@@ -2517,7 +2517,7 @@ 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)
{
@@ -2582,9 +2582,9 @@ 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
@@ -2600,7 +2600,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)
@@ -2635,6 +2635,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;
}
@@ -2696,9 +2698,9 @@ 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
@@ -2712,7 +2714,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 +2754,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 +2923,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;
}
@@ -3011,6 +3017,8 @@ int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_SYSINFO;
LOGP(DCS, LOGL_DEBUG, "free sysinfo ARFCN=%s\n",
gsm_print_arfcn(index2arfcn(cs->arfci)));
+ if (cs->si == cs->list[cs->arfci].sysinfo)
+ cs->si = NULL;
talloc_free(cs->list[cs->arfci].sysinfo);
cs->list[cs->arfci].sysinfo = NULL;
@@ -3106,7 +3114,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.
*/
@@ -3218,7 +3226,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;
@@ -3263,7 +3271,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;
@@ -3323,7 +3331,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;
@@ -3488,7 +3496,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;
}
@@ -4127,7 +4135,8 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
struct gsm48_sysinfo *s;
int i = 0, reselect = 0;
uint16_t acc_class;
- int band, class;
+ int class;
+ enum gsm_band band;
struct gsm322_neighbour *nb;
time_t now;
char arfcn_text[10];
@@ -4180,7 +4189,7 @@ 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));
@@ -4198,7 +4207,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any)
nb->prio_low = 1;
/* get C1 & C2 */
- band = gsm_arfcn2band(nb->arfcn);
+ gsm_arfcn2band_rc(nb->arfcn, &band);
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),
@@ -4353,7 +4362,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;
}
@@ -4414,7 +4423,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. */
@@ -4520,7 +4529,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)
@@ -4695,9 +4704,11 @@ static int gsm322_nb_new_rxlev(struct gsm322_cellsel *cs)
struct llist_head sorted;
struct llist_head *lh, *lh2;
struct gsm48_sysinfo *s = &cs->sel_si;
- int band = gsm_arfcn2band(cs->arfcn);
- int class = class_of_band(cs->ms, band);
+ enum gsm_band band;
+ int class;
+ gsm_arfcn2band_rc(cs->arfcn, &band);
+ class = class_of_band(cs->ms, band);
/* calculate the RAL_C of serving cell */
if (cs->rxlev_count) {
@@ -5075,13 +5086,15 @@ 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)) {
ba = talloc_zero(ms, struct gsm322_ba_list);
- if (!ba)
+ if (!ba) {
+ fclose(fp);
return -ENOMEM;
+ }
rc = fread(buf, 4, 1, fp);
if (!rc) {
talloc_free(ba);
@@ -5138,6 +5151,7 @@ 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;
}
@@ -5156,6 +5170,11 @@ int gsm322_exit(struct osmocom_ms *ms)
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);
}
@@ -5163,12 +5182,7 @@ int gsm322_exit(struct osmocom_ms *ms)
}
}
- if (rc == 2)
- 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));
- else
+ if (rc != 2)
LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
/* free lists */
diff --git a/src/host/layer23/src/mobile/gsm411_sms.c b/src/host/layer23/src/mobile/gsm411_sms.c
index c476ff40..593a2ad3 100644
--- a/src/host/layer23/src/mobile/gsm411_sms.c
+++ b/src/host/layer23/src/mobile/gsm411_sms.c
@@ -125,7 +125,7 @@ static int gsm411_sms_report(struct osmocom_ms *ms, struct gsm_sms *sms,
{
vty_notify(ms, NULL);
if (!cause)
- vty_notify(ms, "SMS to %s successfull\n", sms->address);
+ vty_notify(ms, "SMS to %s successful\n", sms->address);
else
vty_notify(ms, "SMS to %s failed: %s\n", sms->address,
get_value_string(gsm411_rp_cause_strs, cause));
@@ -271,8 +271,8 @@ static int gsm340_rx_tpdu(struct gsm_trans *trans, struct msgb *msg, uint8_t msg
strcpy(gsms->address, "0");
else
gsms->address[0] = '\0';
- gsm48_decode_bcd_number(gsms->address + strlen(gsms->address),
- sizeof(gsms->address) - strlen(gsms->address), address_lv, 1);
+ gsm48_decode_bcd_number2(gsms->address + strlen(gsms->address),
+ sizeof(gsms->address) - strlen(gsms->address), address_lv, sizeof(address_lv), 1);
smsp += oa_len_bytes;
gsms->protocol_id = *smsp++;
@@ -579,6 +579,9 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
sms->address + 1);
else
da_len = gsm340_gen_oa(da, sizeof(da), 0x0, 0x1, sms->address);
+ if (da_len < 0)
+ return da_len;
+
smsp = msgb_put(msg, da_len);
memcpy(smsp, da, da_len);
diff --git a/src/host/layer23/src/mobile/gsm414.c b/src/host/layer23/src/mobile/gsm414.c
new file mode 100644
index 00000000..2f630df3
--- /dev/null
+++ b/src/host/layer23/src/mobile/gsm414.c
@@ -0,0 +1,220 @@
+/*
+ * (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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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/logging.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->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, 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/gsm480_ss.c b/src/host/layer23/src/mobile/gsm480_ss.c
index 116c72f5..4ad2d7cb 100644
--- a/src/host/layer23/src/mobile/gsm480_ss.c
+++ b/src/host/layer23/src/mobile/gsm480_ss.c
@@ -287,7 +287,7 @@ static int gsm480_trans_free(struct gsm_trans *trans)
}
/*
- * endcoding
+ * encoding
*/
#define GSM480_ALLOC_SIZE 512+128
@@ -397,12 +397,17 @@ static int gsm480_tx_release_compl(struct gsm_trans *trans, uint8_t cause)
gh->proto_discr = GSM48_PDISC_NC_SS | (trans->transaction_id << 4);
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+ /* GSM 04.08, section 10.5.4.11 */
if (cause) {
uint8_t *tlv = msgb_put(msg, 4);
- *tlv = GSM48_IE_CAUSE;
- *tlv = 2;
- *tlv = 0x80 | cause;
- *tlv = 0x80 | GSM48_CAUSE_LOC_USER;
+ tlv[0] = GSM48_IE_CAUSE;
+ tlv[1] = 2;
+
+ /* Coding standard defined for the GSM PLMNs,
+ * location - USER, cause as given by caller,
+ * no extension, no diagnostics. */
+ tlv[2] = (1 << 7) | (0x03 << 5) | (GSM48_CAUSE_LOC_USER & 0x0f);
+ tlv[3] = (1 << 7) | cause;
}
return gsm480_to_mm(msg, trans, GSM48_MMSS_DATA_REQ);
}
@@ -438,7 +443,7 @@ static int gsm480_tx_invoke(struct gsm_trans *trans, struct msgb *msg,
if (msg_type == GSM0480_MTYPE_FACILITY)
msgb_wrap_with_L(msg);
else
- msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+ msgb_push_tl(msg, GSM0480_IE_FACILITY);
/* FIXME: If phase 2, we need SSVERSION to be added */
@@ -884,9 +889,9 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data,
strcpy(number, "0");
else
number[0] = '\0';
- gsm48_decode_bcd_number(number + strlen(number),
+ gsm48_decode_bcd_number2(number + strlen(number),
sizeof(number) - strlen(number),
- tag_data - 1, 1);
+ tag_data - 1, tag_len + 1, 1);
vty_notify(ms, "Destination: %s\n", number);
break;
}
@@ -928,7 +933,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;
diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c
index f1e81098..de0d0352 100644
--- a/src/host/layer23/src/mobile/gsm48_cc.c
+++ b/src/host/layer23/src/mobile/gsm48_cc.c
@@ -1750,7 +1750,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 */
diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c
index 02d861e8..adea05db 100644
--- a/src/host/layer23/src/mobile/gsm48_mm.c
+++ b/src/host/layer23/src/mobile/gsm48_mm.c
@@ -48,7 +48,7 @@ extern void *l23_ctx;
void mm_conn_free(struct gsm48_mm_conn *conn);
static int gsm48_rcv_rr(struct osmocom_ms *ms, struct msgb *msg);
static int gsm48_rcv_mmr(struct osmocom_ms *ms, struct msgb *msg);
-static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg);
+int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg);
static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type);
static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms);
static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg);
@@ -109,7 +109,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
@@ -193,7 +193,7 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
*
*
* 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
@@ -1006,7 +1006,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
*/
@@ -2135,7 +2135,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 +2159,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);
}
@@ -2419,7 +2419,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
/* update has finished */
mm->lupd_pending = 0;
- /* RA was successfull */
+ /* RA was successful */
mm->lupd_ra_failure = 0;
/* stop periodic location updating timer */
@@ -2531,7 +2531,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 */
@@ -2674,7 +2674,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;
@@ -2755,7 +2755,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 */
@@ -2938,7 +2938,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
@@ -3395,7 +3395,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);
@@ -3470,7 +3470,7 @@ static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg)
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);
@@ -3583,7 +3583,7 @@ static int gsm48_mm_rel_other(struct osmocom_ms *ms, struct msgb *msg)
*/
static int gsm48_rcv_rr_sapi3(struct osmocom_ms *ms, struct msgb *msg,
- int msg_type, uint8_t sapi)
+ uint32_t msg_type, uint8_t sapi)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
struct gsm48_mm_conn *conn;
@@ -3800,7 +3800,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;
@@ -3895,12 +3895,12 @@ static int gsm48_rcv_rr(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
- int msg_type = rrh->msg_type;
- int sapi = rrh->sapi;
+ uint32_t msg_type = rrh->msg_type;
+ uint8_t sapi = rrh->sapi;
int i, rc;
LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' from RR in state %s "
- "(sapi %d)\n", ms->name, get_rr_name(msg_type),
+ "(sapi %u)\n", ms->name, get_rr_name(msg_type),
gsm48_mm_state_names[mm->state], sapi);
if (sapi)
@@ -3969,7 +3969,7 @@ 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;
- int sapi = rrh->sapi;
+ 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;
@@ -4059,7 +4059,7 @@ 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);
@@ -4076,14 +4076,9 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *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:
msgb_free(msg);
return gsm48_mm_tx_mm_status(ms,
GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
@@ -4300,7 +4295,7 @@ static struct eventstate {
#define EVENTSLLEN \
(sizeof(eventstatelist) / sizeof(struct eventstate))
-static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg)
+int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg)
{
struct gsm48_mmlayer *mm = &ms->mmlayer;
int i, rc;
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c
index c074323f..e115d159 100644
--- a/src/host/layer23/src/mobile/gsm48_rr.c
+++ b/src/host/layer23/src/mobile/gsm48_rr.c
@@ -40,7 +40,7 @@
*
*/
-/* 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.
@@ -71,6 +71,7 @@
#include <osmocom/gsm/rsl.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/bitvec.h>
+#include <osmocom/codec/codec.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/l1l2_interface.h>
@@ -92,6 +93,7 @@ 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_rel_cnf(struct osmocom_ms *ms, struct msgb *msg);
+int gsm414_rcv_test(struct osmocom_ms *ms, const struct msgb *msg);
/*
* support
@@ -210,7 +212,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:
@@ -319,7 +321,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;
@@ -515,8 +517,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;
@@ -634,7 +636,7 @@ static void timeout_rr_meas(void *arg)
uint8_t ch_type, ch_subch, ch_ts;
char text[256];
- /* don't monitor if no cell is selcted or if we scan neighbour cells */
+ /* 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");
goto restart;
@@ -646,7 +648,7 @@ static void timeout_rr_meas(void *arg)
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_rxlev(rxlev), snr, berr,
gsm_print_mcc(cs->sel_mcc),
gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id);
if (rr->state == GSM48_RR_ST_DEDICATED) {
@@ -878,7 +880,7 @@ static void stop_rr_t3126(struct gsm48_rrlayer *rr)
*/
/* 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 +895,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;
@@ -1454,7 +1456,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;
@@ -1516,6 +1518,9 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
int slots;
uint8_t chan_req;
uint8_t tx_power;
+ enum gsm_band band;
+
+ gsm_arfcn2band_rc(cs->arfcn, &band);
/* already assigned */
if (rr->wait_assign == 2)
@@ -1590,11 +1595,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 */
@@ -1657,8 +1662,7 @@ fail:
if (set->alter_tx_power) {
tx_power = set->alter_tx_power_value;
LOGP(DRR, LOGL_INFO, "Use alternative tx-power %d (%d dBm)\n",
- tx_power,
- ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power));
+ tx_power, ms_pwr_dbm(band, tx_power));
} else {
tx_power = s->ms_txpwr_max_cch;
/* power offset in case of DCS1800 */
@@ -1666,15 +1670,12 @@ fail:
&& (cs->arfcn & 1023) <= 885) {
LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value "
"%d (%d dBm) with offset %d dBm\n", tx_power,
- ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power),
- s->po_value * 2);
+ ms_pwr_dbm(band, tx_power), s->po_value * 2);
/* use reserved bits 7,8 for offset (+ X * 2dB) */
tx_power |= s->po_value << 6;
} else
LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value "
- "%d (%d dBm)\n", tx_power,
- ms_pwr_dbm(gsm_arfcn2band(cs->arfcn),
- tx_power));
+ "%d (%d dBm)\n", tx_power, ms_pwr_dbm(band, tx_power));
}
ncch->data[7] = tx_power;
@@ -2078,7 +2079,7 @@ static int gsm48_rr_chan2cause[4] = {
RR_EST_CAUSE_ANS_PAG_TCH_ANY
};
-/* given LV of mobile identity is checked agains ms */
+/* given LV of mobile identity is checked against ms */
static uint8_t gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
{
struct gsm322_cellsel *cs = &ms->cellsel;
@@ -2306,7 +2307,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
} 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
@@ -2336,7 +2337,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
* (immediate) assignment
*/
-/* match request reference agains request history */
+/* match request reference against request history */
static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
@@ -2388,7 +2389,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;
@@ -2496,7 +2497,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;
@@ -2672,7 +2673,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
@@ -2684,7 +2685,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);
@@ -2733,7 +2734,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;
}
@@ -3055,7 +3056,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,
@@ -3414,7 +3415,7 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
}
/*
- * frequency redefition, chanel mode modify, assignment, and handover
+ * frequency redefition, channel mode modify, assignment, and handover
*/
/* set channel mode in case of TCH */
@@ -3433,7 +3434,7 @@ static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr,
/* 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);
+ l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, rr->tch_loop_mode);
return 0;
}
@@ -4538,7 +4539,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;
@@ -4627,14 +4628,16 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
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:
@@ -4674,6 +4677,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 */
@@ -5365,7 +5372,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);
@@ -5503,6 +5510,7 @@ 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;
return 0;
}
@@ -5594,7 +5602,7 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
int s;
if (rr->modify_state != GSM48_RR_MOD_HANDO) {
- LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n");
+ LOGP(DRR, LOGL_NOTICE, "Random access confirm, but not in handover state.\n");
return 0;
}
@@ -5625,25 +5633,67 @@ static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *m
#endif
+#define LOG_FRAME_VERIFY(mode, level, fmt, args...) \
+ LOGP(DRR, level, "Voice frame, mode=%s: " fmt, get_value_string(gsm48_chan_mode_names, mode), ## args)
+
+int voice_frame_verify(enum gsm48_chan_mode mode, uint8_t *frame, size_t frame_len)
+{
+ switch (mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ /* FIXME: this is FR only, check for TCH/F (FR) and TCH/H (HR) */
+ /* RFC 3551, section 4.5.8 GSM */
+ if (frame_len != GSM_FR_BYTES) {
+ LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect length (%zu != %u)\n", frame_len, GSM_FR_BYTES);
+ return -2;
+ }
+ if ((frame[0] >> 4) != 0xd) {
+ LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect magic (%u != 0xd)\n", frame[0] >> 4);
+ return -3;
+ }
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ /* RFC 3551, section 4.5.9 GSM-EFR */
+ if (frame_len != GSM_EFR_BYTES) {
+ LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect length (%zu != %u)\n", frame_len, GSM_EFR_BYTES);
+ return -4;
+ }
+ if ((frame[0] >> 4) != 0xc) {
+ LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect magic (%u != 0xc)\n", frame[0] >> 4);
+ return -5;
+ }
+ break;
+ default:
+ LOG_FRAME_VERIFY(mode, LOGL_ERROR, "not implemented\n");
+ return -1;
+ }
+ return 0;
+}
+
int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm48_rrlayer *rr = &ms->rrlayer;
uint8_t ch_type, ch_subch, ch_ts;
+ struct l1ctl_traffic_req *tr;
if (!rr->dm_est) {
LOGP(DRR, LOGL_INFO, "Current channel is not active\n");
- 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;
+ goto error;
}
+ tr = (struct l1ctl_traffic_req *)msg->l2h;
+ if (voice_frame_verify(rr->cd_now.mode, tr->data, msgb_l2len(msg)) < 0)
+ 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)
@@ -5663,6 +5713,5 @@ int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode)
&& 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->tch_loop_mode);
}
-
diff --git a/src/host/layer23/src/mobile/main.c b/src/host/layer23/src/mobile/main.c
index 297a5349..b1e0940b 100644
--- a/src/host/layer23/src/mobile/main.c
+++ b/src/host/layer23/src/mobile/main.c
@@ -31,7 +31,6 @@
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/application.h>
-#include <osmocom/vty/vty.h>
#include <arpa/inet.h>
@@ -52,8 +51,6 @@ struct llist_head ms_list;
static char *gsmtap_ip = 0;
static const char *custom_cfg_file = NULL;
struct gsmtap_inst *gsmtap_inst = NULL;
-static char *vty_ip = NULL;
-unsigned short vty_port = 4247;
char *config_dir = NULL;
int use_mncc_sock = 0;
int daemonize = 0;
@@ -88,10 +85,6 @@ static void print_help()
printf(" Some help...\n");
printf(" -h --help this text\n");
printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n");
- printf(" -u --vty-ip The VTY IP to telnet to. "
- "(default %s)\n", vty_ip);
- printf(" -v --vty-port The VTY port number to telnet to. "
- "(default %s)\n", vty_get_bind_addr());
printf(" -d --debug Change debug flags. default: %s\n",
debug_default);
printf(" -D --daemonize Run as daemon\n");
@@ -100,19 +93,20 @@ static void print_help()
"offer socket\n");
}
-static void handle_options(int argc, char **argv)
+static int handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"gsmtap-ip", 1, 0, 'i'},
- {"vty-ip", 1, 0, 'u'},
- {"vty-port", 1, 0, 'v'},
{"debug", 1, 0, 'd'},
{"daemonize", 0, 0, 'D'},
{"config-file", 1, 0, 'c'},
{"mncc-sock", 0, 0, 'm'},
+ /* DEPRECATED options, to be removed */
+ {"vty-ip", 1, 0, 'u'},
+ {"vty-port", 1, 0, 'v'},
{0, 0, 0, 0},
};
@@ -130,15 +124,9 @@ static void handle_options(int argc, char **argv)
case 'i':
gsmtap_ip = optarg;
break;
- case 'u':
- vty_ip = optarg;
- break;
case 'c':
custom_cfg_file = optarg;
break;
- case 'v':
- vty_port = atoi(optarg);
- break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
@@ -148,10 +136,20 @@ static void handle_options(int argc, char **argv)
case 'm':
use_mncc_sock = 1;
break;
+ /* DEPRECATED options, to be removed */
+ case 'u':
+ case 'v':
+ fprintf(stderr, "Both 'u' and 'v' options are "
+ "deprecated! Please use the configuration file "
+ "in order to set VTY bind address.\n");
+ /* fall-thru */
default:
- break;
+ /* Unknown parameter passed */
+ return -EINVAL;
}
}
+
+ return 0;
}
void sighandler(int sigset)
@@ -168,7 +166,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 "
@@ -190,9 +188,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);
@@ -219,7 +224,11 @@ int main(int argc, char **argv)
/* Init default stderr logging */
osmo_init_logging2(l23_ctx, &log_info);
- handle_options(argc, argv);
+ 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);
@@ -249,9 +258,9 @@ int main(int argc, char **argv)
config_dir = dirname(config_dir);
if (use_mncc_sock)
- rc = l23_app_init(mncc_recv_socket, config_file, vty_ip ? vty_ip : vty_get_bind_addr(), vty_port);
+ rc = l23_app_init(mncc_recv_socket, config_file);
else
- rc = l23_app_init(NULL, config_file, vty_ip, vty_port);
+ rc = l23_app_init(NULL, config_file);
if (rc)
exit(rc);
diff --git a/src/host/layer23/src/mobile/mncc_sock.c b/src/host/layer23/src/mobile/mncc_sock.c
index d7d56cc0..5ea6feba 100644
--- a/src/host/layer23/src/mobile/mncc_sock.c
+++ b/src/host/layer23/src/mobile/mncc_sock.c
@@ -72,13 +72,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)
@@ -92,11 +92,11 @@ static void mncc_sock_close(struct mncc_sock_state *state)
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 +156,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 +171,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 +195,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 +226,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..9a37b973 100644
--- a/src/host/layer23/src/mobile/mnccms.c
+++ b/src/host/layer23/src/mobile/mnccms.c
@@ -153,7 +153,7 @@ static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver,
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;
LOGP(DMNCC, LOGL_INFO, " support half rate v3\n");
@@ -175,7 +175,7 @@ static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver,
mncc->bearer_cap.speech_ver[i++] = 0;
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;
LOGP(DMNCC, LOGL_INFO, " support half rate v3\n");
@@ -331,7 +331,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
vty_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");
+ vty_notify(ms, "Call: Number invalid or incomplete\n");
break;
case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN:
vty_notify(ms, "Call: No channel available\n");
diff --git a/src/host/layer23/src/mobile/script_lua.c b/src/host/layer23/src/mobile/script_lua.c
index 9117cdd4..50315bdb 100644
--- a/src/host/layer23/src/mobile/script_lua.c
+++ b/src/host/layer23/src/mobile/script_lua.c
@@ -497,12 +497,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 +511,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) {
@@ -544,6 +543,7 @@ static int lua_fd_unregister(lua_State *L) {
static const struct luaL_Reg fd_funcs[] = {
{ "unregister", lua_fd_unregister },
{ "__gc", lua_fd_unregister },
+ { NULL, NULL },
};
static const struct luaL_Reg ms_funcs[] = {
diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/mobile/settings.c
index a4bb4e36..9742b1db 100644
--- a/src/host/layer23/src/mobile/settings.c
+++ b/src/host/layer23/src/mobile/settings.c
@@ -41,6 +41,9 @@ int gsm_settings_init(struct osmocom_ms *ms)
strcpy(set->layer2_socket_path, layer2_socket_path);
strcpy(set->sap_socket_path, sap_socket_path);
+ /* Audio settings: drop TCH frames by default */
+ set->audio.io_handler = AUDIO_IOH_NONE;
+
/* network search */
set->plmn_mode = PLMN_MODE_AUTO;
@@ -49,7 +52,7 @@ int gsm_settings_init(struct osmocom_ms *ms)
sprintf(set->imeisv, "0000000000000000");
/* SIM type */
- set->sim_type = GSM_SIM_TYPE_READER;
+ set->sim_type = GSM_SIM_TYPE_L1PHY;
/* test SIM */
strcpy(set->test_imsi, "001010000000000");
@@ -194,3 +197,8 @@ int gsm_random_imei(struct gsm_settings *set)
return 0;
}
+const struct value_string audio_io_handler_names[] = {
+ { AUDIO_IOH_NONE, "none" },
+ { AUDIO_IOH_LOOPBACK, "loopback" },
+ { 0x00, NULL}
+};
diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/mobile/subscriber.c
index 7a011411..b2eacc59 100644
--- a/src/host/layer23/src/mobile/subscriber.c
+++ b/src/host/layer23/src/mobile/subscriber.c
@@ -29,6 +29,7 @@
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.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>
@@ -347,9 +348,9 @@ static int subscr_sim_smsp(struct osmocom_ms *ms, uint8_t *data,
strcpy(subscr->sms_sca, "+");
if (((smsp->ts_sca[1] & 0x70) >> 4) == 2)
strcpy(subscr->sms_sca, "0");
- gsm48_decode_bcd_number(subscr->sms_sca +
- strlen(subscr->sms_sca), sizeof(subscr->sms_sca)
- - strlen(subscr->sms_sca), smsp->ts_sca, 1);
+ gsm48_decode_bcd_number2(subscr->sms_sca + strlen(subscr->sms_sca),
+ sizeof(subscr->sms_sca) - strlen(subscr->sms_sca),
+ smsp->ts_sca, sizeof(smsp->ts_sca), 1);
}
LOGP(DMM, LOGL_INFO, "received SMSP from SIM (sca=%s)\n",
@@ -711,7 +712,7 @@ void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2,
uint8_t job;
/* skip, if no real valid SIM */
- if (subscr->sim_type != GSM_SIM_TYPE_READER)
+ if (!GSM_SIM_IS_READER(subscr->sim_type))
return;
switch (mode) {
@@ -764,7 +765,7 @@ int gsm_subscr_simcard(struct osmocom_ms *ms)
gsm_subscr_exit(ms);
gsm_subscr_init(ms);
- subscr->sim_type = GSM_SIM_TYPE_READER;
+ subscr->sim_type = GSM_SIM_TYPE_L1PHY;
sprintf(subscr->sim_name, "sim");
subscr->sim_valid = 1;
subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
@@ -790,7 +791,7 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms)
#endif
/* skip, if no real valid SIM */
- if (subscr->sim_type != GSM_SIM_TYPE_READER || !subscr->sim_valid)
+ if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid)
return 0;
/* get tail list from "PLMN not allowed" */
@@ -844,7 +845,7 @@ int gsm_subscr_write_loci(struct osmocom_ms *ms)
struct gsm1111_ef_loci *loci;
/* skip, if no real valid SIM */
- if (subscr->sim_type != GSM_SIM_TYPE_READER || !subscr->sim_valid)
+ if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid)
return 0;
LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n");
@@ -907,8 +908,7 @@ int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq,
struct sim_hdr *nsh;
/* not a SIM */
- if ((subscr->sim_type != GSM_SIM_TYPE_READER
- && subscr->sim_type != GSM_SIM_TYPE_TEST)
+ if (!GSM_SIM_IS_READER(subscr->sim_type)
|| !subscr->sim_valid || no_sim) {
struct gsm48_mm_event *nmme;
@@ -1283,7 +1283,7 @@ int gsm_subscr_sapcard(struct osmocom_ms *ms)
/* Try to connect to the SAP interface */
vty_notify(ms, NULL);
vty_notify(ms, "Connecting to the SAP interface...\n");
- rc = sap_open(ms, ms->settings.sap_socket_path);
+ 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");
@@ -1307,3 +1307,51 @@ int gsm_subscr_remove_sapcard(struct osmocom_ms *ms)
{
return sap_close(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)
+{
+ struct msgb *msg;
+ int rc = 0;
+
+ /* Response parameter is not encoded in case of error */
+ if (res_code != SAP_RESULT_OK_REQ_PROC_CORR)
+ goto ignore_rsp;
+
+ switch (res_type) {
+ case SAP_TRANSFER_APDU_RESP:
+ /* Prevent NULL-pointer dereference */
+ if (!param_len || !param_val) {
+ rc = -EINVAL;
+ goto ignore_rsp;
+ }
+
+ /* FIXME: why do we use this length? */
+ msg = msgb_alloc(GSM_SAP_LENGTH, "sap_apdu");
+ if (!msg) {
+ rc = -ENOMEM;
+ goto ignore_rsp;
+ }
+
+ msg->data = msgb_put(msg, param_len);
+ memcpy(msg->data, param_val, param_len);
+
+ return sim_apdu_resp(ms, msg);
+
+ case SAP_TRANSFER_ATR_RESP:
+ /* TODO: don't read SIM again (if already) */
+ LOGP(DSAP, LOGL_INFO, "SAP card is ready, start reading...\n");
+ return subscr_sim_request(ms);
+
+ default:
+ rc = -ENOTSUP;
+ goto ignore_rsp;
+ }
+
+ return 0;
+
+ignore_rsp:
+ LOGP(DSAP, LOGL_NOTICE, "Ignored SAP response '%s' (code=%d)\n",
+ get_value_string(sap_msg_names, res_type), res_code);
+ return rc;
+}
diff --git a/src/host/layer23/src/mobile/voice.c b/src/host/layer23/src/mobile/voice.c
index b7678337..c3792650 100644
--- a/src/host/layer23/src/mobile/voice.c
+++ b/src/host/layer23/src/mobile/voice.c
@@ -22,7 +22,9 @@
#include <stdlib.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/codec/codec.h>
+#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/voice.h>
@@ -36,16 +38,36 @@ static int gsm_recv_voice(struct osmocom_ms *ms, struct msgb *msg)
{
struct gsm_data_frame *mncc;
+ /* Drop the l1ctl_info_dl header */
+ msgb_pull_to_l2(msg);
+ /* push mncc header in front of data */
+ mncc = (struct gsm_data_frame *)
+ msgb_push(msg, sizeof(struct gsm_data_frame));
+ mncc->callref = ms->mncc_entity.ref;
+
+ /* FIXME: FR, EFR only! */
+ switch (ms->rrlayer.cd_now.mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ mncc->msg_type = GSM_TCHF_FRAME;
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ mncc->msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ default:
+ /* TODO: print error message here */
+ goto exit_free;
+ }
+
+ /* send voice frame back, if appropriate */
+ if (ms->settings.audio.io_handler == AUDIO_IOH_LOOPBACK)
+ gsm_send_voice(ms, mncc);
+
/* distribute and then free */
if (ms->mncc_entity.mncc_recv && ms->mncc_entity.ref) {
- /* 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);
}
+exit_free:
msgb_free(msg);
return 0;
}
@@ -56,12 +78,26 @@ static int gsm_recv_voice(struct osmocom_ms *ms, struct msgb *msg)
int gsm_send_voice(struct osmocom_ms *ms, struct gsm_data_frame *data)
{
struct msgb *nmsg;
+ int len;
+
+ switch (ms->rrlayer.cd_now.mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ /* FIXME: FR only, check for TCH/F (FR) and TCH/H (HR) */
+ len = GSM_FR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ len = GSM_EFR_BYTES;
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "gsm_send_voice, msg_type=0x%02x: not implemented\n", data->msg_type);
+ return -EINVAL;
+ }
- nmsg = msgb_alloc_headroom(33 + 64, 64, "TCH/F");
+ nmsg = msgb_alloc_headroom(len + 64, 64, "TCH/F");
if (!nmsg)
return -ENOMEM;
- nmsg->l2h = msgb_put(nmsg, 33);
- memcpy(nmsg->l2h, data->data, 33);
+ nmsg->l2h = msgb_put(nmsg, len);
+ memcpy(nmsg->l2h, data->data, len);
return gsm48_rr_tx_voice(ms, nmsg);
}
diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c
index 171a47ce..05472314 100644
--- a/src/host/layer23/src/mobile/vty_interface.c
+++ b/src/host/layer23/src/mobile/vty_interface.c
@@ -65,6 +65,12 @@ struct cmd_node support_node = {
1
};
+struct cmd_node audio_node = {
+ AUDIO_NODE,
+ "%s(audio)# ",
+ 1
+};
+
static void print_vty(void *priv, const char *fmt, ...)
{
char buffer[1000];
@@ -223,7 +229,7 @@ static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty)
gsm_get_mnc(ms->cellsel.sel_mcc, ms->cellsel.sel_mnc),
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]);
@@ -455,33 +461,29 @@ DEFUN(clone_tsmi, clone_tsmi_cmd, "clone tmsi MS_NAME [TMSI]",
return CMD_SUCCESS;
}
-DEFUN(clone_imsi, clone_imsi_cmd, "clone imsi MS_NAME [IMSI]",
+DEFUN(clone_imsi, clone_imsi_cmd, "clone imsi MS_NAME IMSI",
"Spoof mobile identity\n"
"Force MS to use specified IMSI\n"
"Name of MS (see \"show ms\")\n"
"Specify required IMSI (15 digits)")
{
+ const char *imsi = argv[1];
struct osmocom_ms *ms;
- const char *imsi;
char *error;
ms = get_ms(argv[0], vty);
if (!ms)
return CMD_WARNING;
- if (argc >= 2) {
- imsi = argv[1];
-
- error = gsm_check_imsi(imsi);
- if (error) {
- vty_out(vty, "%s%s", error, VTY_NEWLINE);
- return CMD_WARNING;
- }
+ error = gsm_check_imsi(imsi);
+ if (error) {
+ vty_out(vty, "%s%s", error, VTY_NEWLINE);
+ return CMD_WARNING;
}
- strncpy(ms->subscr.imsi, imsi, GSM_IMSI_LENGTH);
+ osmo_strlcpy(ms->subscr.imsi, imsi, GSM_IMSI_LENGTH);
vty_out(vty, "Forced to use the following IMSI: %s%s",
- imsi, VTY_NEWLINE);
+ ms->subscr.imsi, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -600,7 +602,7 @@ static int _sim_test_cmd(struct vty *vty, int argc, const char *argv[],
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"
+ "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"
@@ -611,7 +613,7 @@ DEFUN(sim_test, sim_test_cmd,
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"
+ "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")
@@ -891,7 +893,7 @@ DEFUN(network_select, network_select_cmd,
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"
+ "Make an emergency call\nAnswer an incoming call\nHangup a call\n"
"Hold current active call\n")
{
struct osmocom_ms *ms;
@@ -1079,6 +1081,28 @@ DEFUN(test_reselection, test_reselection_cmd, "test re-selection NAME",
return CMD_SUCCESS;
}
+DEFUN(test_lur, test_lur_cmd, "test location-update NAME",
+ "Manually trigger Location Update procedure\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+ struct gsm48_mmlayer *mm;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ /* Reset attempt counter when attempting to update (4.4.4.5) */
+ mm = &ms->mmlayer;
+ if (mm->state == GSM48_MM_ST_MM_IDLE
+ && mm->substate == GSM48_MM_SST_ATTEMPT_UPDATE)
+ mm->lupd_attempt = 0;
+
+ gsm48_mm_ev(ms, GSM48_MM_EVENT_TIMEOUT_T3212, NULL);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(delete_forbidden_plmn, delete_forbidden_plmn_cmd,
"delete forbidden plmn NAME MCC MNC",
"Delete\nForbidden\nplmn\nName of MS (see \"show ms\")\n"
@@ -1400,7 +1424,7 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
case GSM_SIM_TYPE_NONE:
vty_out(vty, " sim none%s", VTY_NEWLINE);
break;
- case GSM_SIM_TYPE_READER:
+ case GSM_SIM_TYPE_L1PHY:
vty_out(vty, " sim reader%s", VTY_NEWLINE);
break;
case GSM_SIM_TYPE_TEST:
@@ -1603,6 +1627,10 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
vty_out(vty, " c7-any-timeout %d%s",
set->any_timeout, VTY_NEWLINE);
+ vty_out(vty, " audio%s", VTY_NEWLINE);
+ if (!hide_default || set->audio.io_handler != AUDIO_IOH_NONE)
+ vty_out(vty, " io-handler %s%s", audio_io_handler_name(set->audio.io_handler), VTY_NEWLINE);
+
/* no shutdown must be written to config, because shutdown is default */
vty_out(vty, " %sshutdown%s", (ms->shutdown != MS_SHUTDOWN_NONE) ? "" : "no ",
VTY_NEWLINE);
@@ -1674,7 +1702,7 @@ DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH",
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 from reader\nAttach build in test SIM\n"
"Attach SIM over SAP interface")
{
struct osmocom_ms *ms = vty->index;
@@ -1685,7 +1713,7 @@ DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test|sap)",
set->sim_type = GSM_SIM_TYPE_NONE;
break;
case 'r':
- set->sim_type = GSM_SIM_TYPE_READER;
+ set->sim_type = GSM_SIM_TYPE_L1PHY;
break;
case 't':
set->sim_type = GSM_SIM_TYPE_TEST;
@@ -2804,6 +2832,46 @@ DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-coun
return CMD_SUCCESS;
}
+/* per audio config */
+DEFUN(cfg_ms_audio, cfg_ms_audio_cmd, "audio",
+ "Configure audio settings")
+{
+ vty->node = AUDIO_NODE;
+ return CMD_SUCCESS;
+}
+
+static int set_audio_io_handler(struct vty *vty, enum audio_io_handler val)
+{
+ struct osmocom_ms *ms = (struct osmocom_ms *) vty->index;
+ struct gsm_settings *set = &ms->settings;
+
+ /* Don't restart on unchanged value */
+ if (val == set->audio.io_handler)
+ return CMD_SUCCESS;
+ set->audio.io_handler = val;
+
+ /* Restart required */
+ vty_restart_if_started(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_audio_io_handler, cfg_ms_audio_io_handler_cmd,
+ "io-handler (loopback|none)",
+ "Set TCH frame I/O handler\n"
+ "Return TCH frame payload back to sender\n"
+ "No handler, drop TCH frames (default)")
+{
+ int val = get_string_value(audio_io_handler_names, argv[0]);
+ return set_audio_io_handler(vty, val);
+}
+
+DEFUN(cfg_ms_audio_no_io_handler, cfg_ms_audio_no_io_handler_cmd,
+ "no io-handler", NO_STR "Disable TCH frame processing")
+{
+ return set_audio_io_handler(vty, AUDIO_IOH_NONE);
+}
+
DEFUN(cfg_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown",
NO_STR "Activate and run MS")
{
@@ -2884,6 +2952,7 @@ int ms_vty_go_parent(struct vty *vty)
break;
case TESTSIM_NODE:
case SUPPORT_NODE:
+ case AUDIO_NODE:
vty->node = MS_NODE;
break;
default:
@@ -2939,6 +3008,7 @@ int ms_vty_init(void)
install_element(ENABLE_NODE, &sms_cmd);
install_element(ENABLE_NODE, &service_cmd);
install_element(ENABLE_NODE, &test_reselection_cmd);
+ install_element(ENABLE_NODE, &test_lur_cmd);
install_element(ENABLE_NODE, &delete_forbidden_plmn_cmd);
install_element(ENABLE_NODE, &clone_tsmi_cmd);
install_element(ENABLE_NODE, &clone_imsi_cmd);
@@ -2998,6 +3068,7 @@ int ms_vty_init(void)
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_audio_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);
@@ -3075,6 +3146,10 @@ int ms_vty_init(void)
install_element(MS_NODE, &cfg_ms_script_load_run_cmd);
install_element(MS_NODE, &cfg_ms_no_script_load_run_cmd);
+ install_node(&audio_node, config_write_dummy);
+ install_element(AUDIO_NODE, &cfg_ms_audio_io_handler_cmd);
+ install_element(AUDIO_NODE, &cfg_ms_audio_no_io_handler_cmd);
+
/* Register the talloc context introspection command */
osmo_talloc_vty_add_cmds();
diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c
index 26416f76..d49c3fdf 100644
--- a/src/host/osmocon/osmocon.c
+++ b/src/host/osmocon/osmocon.c
@@ -287,9 +287,15 @@ int read_file(const char *filename, int chainload)
}
rc = fstat(fd, &st);
+ if (rc < 0) {
+ perror("fstat");
+ close(fd);
+ return -EIO;
+ }
if ((st.st_size > MAX_DNLOAD_SIZE) && (dnload.mode != MODE_ROMLOAD)) {
fprintf(stderr, "The maximum file size is 64kBytes (%u bytes)\n",
MAX_DNLOAD_SIZE);
+ close(fd);
return -EFBIG;
}
} else {
@@ -494,7 +500,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;
}
@@ -580,7 +586,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",
@@ -624,6 +630,7 @@ static int handle_write_dnload(void)
case MODE_C140xor:
case MODE_C123xor:
rc = write(dnload.serial_fd.fd, &xor_init, 1);
+ OSMO_ASSERT(rc == 1);
break;
default:
break;
@@ -631,7 +638,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;
}
@@ -646,6 +653,7 @@ static int handle_write_dnload(void)
perror("Error during write");
return rc;
}
+ OSMO_ASSERT(rc == write_len);
dnload.write_ptr += rc;
@@ -674,7 +682,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;
}
@@ -743,7 +751,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)
@@ -834,12 +842,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;
@@ -860,18 +868,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;
}
@@ -992,7 +1000,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;
@@ -1108,7 +1116,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:
@@ -1130,7 +1138,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:
@@ -1148,7 +1156,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;
@@ -1164,7 +1172,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);
@@ -1180,7 +1188,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;
@@ -1242,7 +1250,7 @@ static int un_tool_read(struct osmo_fd *fd, unsigned int flags)
c = 0;
while(c < 2) {
- rc = read(fd->fd, &buf + c, 2 - c);
+ rc = read(fd->fd, buf + c, 2 - c);
if(rc == 0) {
// disconnect
goto close;
@@ -1262,7 +1270,7 @@ static int un_tool_read(struct osmo_fd *fd, unsigned int flags)
c = 0;
while(c < length) {
- rc = read(fd->fd, &buf + c, length - c);
+ rc = read(fd->fd, buf + c, length - c);
if(rc == 0) {
// disconnect
goto close;
@@ -1308,17 +1316,17 @@ static int tool_accept(struct osmo_fd *fd, unsigned int flags)
con = talloc_zero(NULL, struct tool_connection);
if (!con) {
fprintf(stderr, "Failed to create tool connection.\n");
+ close(rc);
return -1;
}
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);
+ close(rc);
return -1;
}
@@ -1378,7 +1386,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";
@@ -1431,12 +1439,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);
@@ -1447,9 +1456,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/tpu_debug.c b/src/host/osmocon/tpu_debug.c
index c9dac903..9160b014 100644
--- a/src/host/osmocon/tpu_debug.c
+++ b/src/host/osmocon/tpu_debug.c
@@ -57,6 +57,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 +147,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 +174,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/l1ctl.c b/src/host/trxcon/l1ctl.c
index 83cdc2e4..8083595f 100644
--- a/src/host/trxcon/l1ctl.c
+++ b/src/host/trxcon/l1ctl.c
@@ -37,8 +37,8 @@
#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 "trxcon.h"
#include "logging.h"
#include "l1ctl_link.h"
#include "l1ctl_proto.h"
@@ -46,6 +46,16 @@
#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;
@@ -80,7 +90,7 @@ int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn,
return -ENOMEM;
LOGP(DL1C, LOGL_DEBUG, "Send PM Conf (%s %d = %d dBm)\n",
- gsm_band_name(gsm_arfcn2band(band_arfcn)),
+ arfcn2band_name(band_arfcn),
band_arfcn &~ ARFCN_FLAG_MASK, dbm);
pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
@@ -129,37 +139,52 @@ int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t 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 l1ctl_info_dl *dl;
struct msgb *msg;
- size_t len;
msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
if (msg == NULL)
return -ENOMEM;
- LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=%u, bsic=%u)\n",
- result, bsic);
-
- /* Copy DL info provided by handler */
- len = sizeof(struct l1ctl_info_dl);
- dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
- memcpy(dl, dl_info, len);
+ put_dl_info_hdr(msg, dl_info);
talloc_free(dl_info);
- /* Fill in FBSB payload: BSIC and sync result */
- conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf));
- conf->result = result;
- conf->bsic = bsic;
+ 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 = 1;
+ l1l->fbsb_conf_sent = true;
/* Abort FBSB expire timer */
if (osmo_timer_pending(&l1l->fbsb_timer))
@@ -189,7 +214,6 @@ int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode)
int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
uint8_t *l2, size_t l2_len, bool traffic)
{
- struct l1ctl_info_dl *dl;
struct msgb *msg;
uint8_t *msg_l2;
@@ -198,9 +222,7 @@ int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
if (msg == NULL)
return -ENOMEM;
- /* Copy DL header */
- dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl));
- memcpy(dl, data, sizeof(*dl));
+ put_dl_info_hdr(msg, data);
/* Copy the L2 payload if preset */
if (l2 && l2_len > 0) {
@@ -212,21 +234,20 @@ int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
return l1ctl_link_send(l1l, msg);
}
-int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn)
+int l1ctl_tx_rach_conf(struct l1ctl_link *l1l,
+ uint16_t band_arfcn, uint32_t fn)
{
struct l1ctl_info_dl *dl;
struct msgb *msg;
- size_t len;
msg = l1ctl_alloc_msg(L1CTL_RACH_CONF);
if (msg == NULL)
return -ENOMEM;
- len = sizeof(struct l1ctl_info_dl);
- dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
+ dl = put_dl_info_hdr(msg, NULL);
+ memset(dl, 0x00, sizeof(*dl));
- memset(dl, 0x00, len);
- dl->band_arfcn = htons(l1l->trx->band_arfcn);
+ dl->band_arfcn = htons(band_arfcn);
dl->frame_nr = htonl(fn);
return l1ctl_link_send(l1l, msg);
@@ -239,9 +260,7 @@ int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn)
int l1ctl_tx_dt_conf(struct l1ctl_link *l1l,
struct l1ctl_info_dl *data, bool traffic)
{
- struct l1ctl_info_dl *dl;
struct msgb *msg;
- size_t len;
msg = l1ctl_alloc_msg(traffic ?
L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF);
@@ -249,9 +268,7 @@ int l1ctl_tx_dt_conf(struct l1ctl_link *l1l,
return -ENOMEM;
/* Copy DL frame header from source message */
- len = sizeof(struct l1ctl_info_dl);
- dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
- memcpy(dl, data, len);
+ put_dl_info_hdr(msg, data);
return l1ctl_link_send(l1l, msg);
}
@@ -280,32 +297,24 @@ static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mod
static void fbsb_timer_cb(void *data)
{
struct l1ctl_link *l1l = (struct l1ctl_link *) data;
- struct l1ctl_fbsb_conf *conf;
struct l1ctl_info_dl *dl;
struct msgb *msg;
- size_t len;
msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
if (msg == NULL)
return;
- LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=255, bsic=0)\n");
+ LOGP(DL1C, LOGL_NOTICE, "FBSB timer fired for ARFCN %u\n", l1l->trx->band_arfcn &~ ARFCN_FLAG_MASK);
- /* Compose DL info header */
- len = sizeof(struct l1ctl_info_dl);
- dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
- memset(dl, 0x00, len);
+ dl = put_dl_info_hdr(msg, NULL);
/* Fill in current ARFCN */
dl->band_arfcn = htons(l1l->trx->band_arfcn);
- /* Fill in FBSB payload: BSIC and sync result */
- conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf));
- conf->result = 255;
- conf->bsic = 0;
+ fbsb_conf_make(msg, 255, 0);
/* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
- l1l->fbsb_conf_sent = 1;
+ l1l->fbsb_conf_sent = true;
l1ctl_link_send(l1l, msg);
}
@@ -331,17 +340,17 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg)
timeout = ntohs(fbsb->timeout);
LOGP(DL1C, LOGL_NOTICE, "Received FBSB request (%s %d)\n",
- gsm_band_name(gsm_arfcn2band(band_arfcn)),
+ arfcn2band_name(band_arfcn),
band_arfcn &~ ARFCN_FLAG_MASK);
/* Reset scheduler and clock counter */
- sched_trx_reset(l1l->trx, 1);
+ 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 = 0;
+ l1l->fbsb_conf_sent = false;
/* Only if current ARFCN differs */
if (l1l->trx->band_arfcn != band_arfcn) {
@@ -353,13 +362,17 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg)
trx_if_cmd_txtune(l1l->trx, band_arfcn);
}
- trx_if_cmd_poweron(l1l->trx);
+ /* Transceiver might have been powered on before, e.g.
+ * in case of sending L1CTL_FBSB_REQ due to signal loss. */
+ if (!l1l->trx->powered_up)
+ trx_if_cmd_poweron(l1l->trx);
/* Start FBSB expire timer */
l1l->fbsb_timer.data = l1l;
l1l->fbsb_timer.cb = fbsb_timer_cb;
+ LOGP(DL1C, LOGL_INFO, "Starting FBSB timer %u ms\n", timeout * GSM_TDMA_FN_DURATION_uS / 1000);
osmo_timer_schedule(&l1l->fbsb_timer, 0,
- timeout * FRAME_DURATION_uS);
+ timeout * GSM_TDMA_FN_DURATION_uS);
exit:
msgb_free(msg);
@@ -385,7 +398,7 @@ static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg)
LOGP(DL1C, LOGL_NOTICE, "Received power measurement "
"request (%s: %d -> %d)\n",
- gsm_band_name(gsm_arfcn2band(band_arfcn_start)),
+ arfcn2band_name(band_arfcn_start),
band_arfcn_start &~ ARFCN_FLAG_MASK,
band_arfcn_stop &~ ARFCN_FLAG_MASK);
@@ -421,7 +434,7 @@ static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg)
/* Fall through */
case L1CTL_RES_T_SCHED:
- sched_trx_reset(l1l->trx, 1);
+ sched_trx_reset(l1l->trx, true);
break;
default:
LOGP(DL1C, LOGL_ERROR, "Unknown L1CTL_RESET_REQ type\n");
@@ -493,97 +506,145 @@ exit:
return rc;
}
-static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg)
+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;
- uint8_t chan_nr, link_id;
size_t len;
int rc;
ul = (struct l1ctl_info_ul *) msg->l1h;
- req = (struct l1ctl_rach_req *) ul->payload;
- len = sizeof(struct l1ctl_rach_req);
-
- /* Convert offset value to host format */
- req->offset = ntohs(req->offset);
- /**
- * FIXME: l1ctl_info_ul doesn't provide channel description
- * FIXME: Can we use other than TS0?
- */
- chan_nr = 0x88;
- link_id = 0x00;
+ /* 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);
+ }
- LOGP(DL1C, LOGL_NOTICE, "Received 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, chan_nr, link_id);
+ rc = sched_prim_init(l1l->trx, &prim, len, ul->chan_nr, ul->link_id);
if (rc)
goto exit;
/**
- * Push this primitive to transmit queue
- *
- * FIXME: what if requested TS is not configured?
- * Or what if one (such as TCH) has no TRXC_RACH slots?
+ * Push this primitive to the transmit queue.
+ * Indicated timeslot needs to be configured.
*/
- rc = sched_prim_push(l1l->trx, prim, chan_nr);
+ rc = sched_prim_push(l1l->trx, prim, ul->chan_nr);
if (rc) {
talloc_free(prim);
goto exit;
}
/* Fill in the payload */
- memcpy(prim->payload, req, len);
+ memcpy(prim->payload, ul->payload, len);
exit:
msgb_free(msg);
return rc;
}
+static int l1ctl_proc_est_req_h0(struct trx_instance *trx, struct l1ctl_h0 *h)
+{
+ uint16_t band_arfcn;
+ int rc = 0;
+
+ band_arfcn = ntohs(h->band_arfcn);
+
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a single "
+ "ARFCN=%u channel\n", band_arfcn &~ ARFCN_FLAG_MASK);
+
+ /* Do we need to retune? */
+ if (trx->band_arfcn == band_arfcn)
+ return 0;
+
+ /* Tune transceiver to required ARFCN */
+ rc |= trx_if_cmd_rxtune(trx, band_arfcn);
+ rc |= trx_if_cmd_txtune(trx, band_arfcn);
+ if (rc)
+ return rc;
+
+ /* Update current ARFCN */
+ trx->band_arfcn = band_arfcn;
+
+ return 0;
+}
+
+static int l1ctl_proc_est_req_h1(struct trx_instance *trx, struct l1ctl_h1 *h)
+{
+ uint16_t ma[64];
+ int i, rc;
+
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a Frequency "
+ "Hopping (hsn=%u, maio=%u, chans=%u) channel\n",
+ h->hsn, h->maio, h->n);
+
+ /* No channels?!? */
+ if (!h->n) {
+ LOGP(DL1C, LOGL_ERROR, "No channels in mobile allocation?!?\n");
+ return -EINVAL;
+ } else if (h->n > ARRAY_SIZE(ma)) {
+ LOGP(DL1C, LOGL_ERROR, "More than 64 channels in mobile allocation?!?\n");
+ return -EINVAL;
+ }
+
+ /* Convert from network to host byte order */
+ for (i = 0; i < h->n; i++)
+ ma[i] = ntohs(h->ma[i]);
+
+ /* Forward hopping parameters to TRX */
+ rc = trx_if_cmd_setfh(trx, h->hsn, h->maio, ma, h->n);
+ if (rc)
+ return rc;
+
+ /**
+ * TODO: update the state of trx_instance somehow
+ * in order to indicate that it is in hopping mode...
+ */
+ return 0;
+}
+
static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg)
{
enum gsm_phys_chan_config config;
struct l1ctl_dm_est_req *est_req;
struct l1ctl_info_ul *ul;
struct trx_ts *ts;
- uint16_t band_arfcn;
uint8_t chan_nr, tn;
- int rc = 0;
+ int rc;
ul = (struct l1ctl_info_ul *) msg->l1h;
est_req = (struct l1ctl_dm_est_req *) ul->payload;
- band_arfcn = ntohs(est_req->h0.band_arfcn);
chan_nr = ul->chan_nr;
tn = chan_nr & 0x07;
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_EST_REQ (arfcn=%u, "
- "tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n",
- (band_arfcn &~ ARFCN_FLAG_MASK), tn, chan_nr,
- est_req->tsc, est_req->tch_mode);
-
- if (est_req->h) {
- LOGP(DL1C, LOGL_ERROR, "FHSS is not supported\n");
- rc = -ENOTSUP;
- goto exit;
- }
-
- /* Only if the 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);
- }
-
- /* Update TSC (Training Sequence Code) */
- l1l->trx->tsc = est_req->tsc;
+ 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);
@@ -593,6 +654,17 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg)
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];
@@ -623,7 +695,7 @@ static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg)
"switching back to CCCH\n");
/* Reset scheduler */
- sched_trx_reset(l1l->trx, 0);
+ sched_trx_reset(l1l->trx, false);
msgb_free(msg);
return 0;
@@ -736,8 +808,11 @@ static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg)
/* TODO: do we need to care about audio_mode? */
- msgb_free(msg);
- return 0;
+ /* Re-use the original message as confirmation */
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ l1h->msg_type = L1CTL_TCH_MODE_CONF;
+
+ return l1ctl_link_send(l1l, msg);
}
static int l1ctl_rx_crypto_req(struct l1ctl_link *l1l, struct msgb *msg)
@@ -797,7 +872,9 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *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);
+ 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:
@@ -821,6 +898,7 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg)
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,
diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h
index ca8c0be6..48bbe097 100644
--- a/src/host/trxcon/l1ctl.h
+++ b/src/host/trxcon/l1ctl.h
@@ -22,4 +22,5 @@ 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, uint32_t fn);
+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
index 20cb70ce..4c406d6c 100644
--- a/src/host/trxcon/l1ctl_link.c
+++ b/src/host/trxcon/l1ctl_link.c
@@ -165,9 +165,7 @@ static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags)
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;
+ osmo_fd_setup(conn_bfd, cfd, OSMO_FD_READ, osmo_wqueue_bfd_cb, l1l, 0);
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to register new connection fd\n");
@@ -186,7 +184,7 @@ static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags)
int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg)
{
- uint16_t *len;
+ uint8_t *len;
/* Debug print */
LOGP(DL1D, LOGL_DEBUG, "TX: '%s'\n",
@@ -196,8 +194,8 @@ int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg)
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);
+ len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
+ osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
if (osmo_wqueue_enqueue(&l1l->wq, msg) != 0) {
LOGP(DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
@@ -229,53 +227,56 @@ int l1ctl_link_close_conn(struct l1ctl_link *l1l)
return 0;
}
-int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path)
+struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path)
{
- struct l1ctl_link *l1l_new;
+ struct l1ctl_link *l1l;
struct osmo_fd *bfd;
int rc;
LOGP(DL1C, LOGL_NOTICE, "Init L1CTL link (%s)\n", sock_path);
- l1l_new = talloc_zero(tall_trx_ctx, struct l1ctl_link);
- if (!l1l_new) {
+ l1l = talloc_zero(tall_ctx, struct l1ctl_link);
+ if (!l1l) {
LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n");
- return -ENOMEM;
+ 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_new->listen_bfd;
+ bfd = &l1l->listen_bfd;
+
+ /* Bind connection handler */
+ osmo_fd_setup(bfd, -1, OSMO_FD_READ, l1ctl_link_accept, l1l, 0);
+
rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path,
OSMO_SOCK_F_BIND);
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n",
strerror(errno));
- talloc_free(l1l_new);
- return rc;
+ osmo_fsm_inst_free(l1l->fsm);
+ talloc_free(l1l);
+ return NULL;
}
/* Bind shutdown handler */
- l1l_new->shutdown_cb = l1ctl_shutdown_cb;
-
- /* Bind connection handler */
- bfd->cb = l1ctl_link_accept;
- bfd->when = BSC_FD_READ;
- bfd->data = l1l_new;
+ l1l->shutdown_cb = l1ctl_shutdown_cb;
/**
* To be able to accept first connection and
* drop others, it should be set to -1
*/
- l1l_new->wq.bfd.fd = -1;
-
- /* Allocate a new dedicated state machine */
- osmo_fsm_register(&l1ctl_fsm);
- l1l_new->fsm = osmo_fsm_inst_alloc(&l1ctl_fsm, l1l_new,
- NULL, LOGL_DEBUG, "l1ctl_link");
-
- *l1l = l1l_new;
+ l1l->wq.bfd.fd = -1;
- return 0;
+ return l1l;
}
void l1ctl_link_shutdown(struct l1ctl_link *l1l)
@@ -308,3 +309,8 @@ void l1ctl_link_shutdown(struct l1ctl_link *l1l)
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
index 01103dcc..a333e407 100644
--- a/src/host/trxcon/l1ctl_link.h
+++ b/src/host/trxcon/l1ctl_link.h
@@ -35,13 +35,13 @@ struct l1ctl_link {
/* L1CTL handlers specific */
struct osmo_timer_list fbsb_timer;
- uint8_t fbsb_conf_sent;
+ bool fbsb_conf_sent;
/* Shutdown callback */
void (*shutdown_cb)(struct l1ctl_link *l1l);
};
-int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path);
+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);
diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c
index 6a3043bb..78915f21 100644
--- a/src/host/trxcon/logging.c
+++ b/src/host/trxcon/logging.c
@@ -26,7 +26,6 @@
#include <osmocom/core/utils.h>
#include "logging.h"
-#include "trxcon.h"
static struct log_info_cat trx_log_info_cat[] = {
[DAPP] = {
@@ -78,9 +77,9 @@ static const struct log_info trx_log_info = {
.num_cat = ARRAY_SIZE(trx_log_info_cat),
};
-int trx_log_init(const char *category_mask)
+int trx_log_init(void *tall_ctx, const char *category_mask)
{
- osmo_init_logging2(tall_trx_ctx, &trx_log_info);
+ osmo_init_logging2(tall_ctx, &trx_log_info);
if (category_mask)
log_parse_category_mask(osmo_stderr_target, category_mask);
diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h
index 0206362a..152c3467 100644
--- a/src/host/trxcon/logging.h
+++ b/src/host/trxcon/logging.h
@@ -14,4 +14,4 @@ enum {
DSCHD,
};
-int trx_log_init(const char *category_mask);
+int trx_log_init(void *tall_ctx, const char *category_mask);
diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c
index d4054c99..9476ccd7 100644
--- a/src/host/trxcon/sched_clck.c
+++ b/src/host/trxcon/sched_clck.c
@@ -41,7 +41,6 @@
#include "scheduler.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.h"
#define MAX_FN_SKEW 50
#define TRX_LOSS_FRAMES 400
@@ -51,7 +50,7 @@ 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 };
+ const struct timespec frame_duration = { .tv_sec = 0, .tv_nsec = GSM_TDMA_FN_DURATION_nS };
/* Check if transceiver is still alive */
if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) {
@@ -69,7 +68,7 @@ static void sched_clck_tick(void *data)
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) {
+ if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) {
LOGP(DSCH, LOGL_NOTICE, "PC clock skew: "
"elapsed uS %" PRId64 "\n", elapsed_us);
@@ -79,11 +78,11 @@ static void sched_clck_tick(void *data)
}
/* Schedule next FN clock */
- while (elapsed_us > FRAME_DURATION_uS / 2) {
+ while (elapsed_us > GSM_TDMA_FN_DURATION_uS / 2) {
timespecadd(tv_clock, &frame_duration, tv_clock);
- elapsed_us -= FRAME_DURATION_uS;
+ elapsed_us -= GSM_TDMA_FN_DURATION_uS;
- sched->fn_counter_proc = TDMA_FN_INC(sched->fn_counter_proc);
+ GSM_TDMA_FN_INC(sched->fn_counter_proc);
/* Call frame callback */
if (sched->clock_cb)
@@ -91,7 +90,7 @@ static void sched_clck_tick(void *data)
}
osmo_timer_schedule(&sched->clock_timer, 0,
- FRAME_DURATION_uS - elapsed_us);
+ GSM_TDMA_FN_DURATION_uS - elapsed_us);
}
static void sched_clck_correct(struct trx_sched *sched,
@@ -109,7 +108,7 @@ static void sched_clck_correct(struct trx_sched *sched,
sched->clock_timer.cb = sched_clck_tick;
sched->clock_timer.data = sched;
- osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS);
+ osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS);
}
int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
@@ -141,10 +140,10 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
/* 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);
+ elapsed_fn = GSM_TDMA_FN_SUB(fn, sched->fn_counter_proc);
if (elapsed_fn >= 135774)
- elapsed_fn -= GSM_HYPERFRAME;
+ elapsed_fn -= GSM_TDMA_HYPERFRAME;
/* Check for max clock skew */
if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
@@ -156,7 +155,7 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
}
LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %" PRId64 "\n",
- elapsed_fn * FRAME_DURATION_uS - elapsed_us);
+ elapsed_fn * GSM_TDMA_FN_DURATION_uS - elapsed_us);
/* Too many frames have been processed already */
if (elapsed_fn < 0) {
@@ -165,21 +164,21 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
* Set clock to the time or last FN should
* have been transmitted
*/
- duration.tv_nsec = (0 - elapsed_fn) * FRAME_DURATION_uS * 1000;
+ duration.tv_nsec = (0 - elapsed_fn) * GSM_TDMA_FN_DURATION_nS;
duration.tv_sec = duration.tv_nsec / 1000000000;
duration.tv_nsec = duration.tv_nsec % 1000000000;
timespecadd(&tv_now, &duration, tv_clock);
/* Set time to the time our next FN has to be transmitted */
osmo_timer_schedule(&sched->clock_timer, 0,
- FRAME_DURATION_uS * (1 - elapsed_fn));
+ GSM_TDMA_FN_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);
+ GSM_TDMA_FN_INC(sched->fn_counter_proc);
/* Call frame callback */
if (sched->clock_cb)
@@ -188,7 +187,7 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
/* Schedule next FN to be transmitted */
*tv_clock = tv_now;
- osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS);
+ osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS);
return 0;
}
diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c
index 95e496b2..ae43ca94 100644
--- a/src/host/trxcon/sched_lchan_common.c
+++ b/src/host/trxcon/sched_lchan_common.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: common routines for lchan handlers
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -32,17 +32,20 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/bits.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
#include <osmocom/codec/codec.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
#include "l1ctl_proto.h"
#include "scheduler.h"
#include "sched_trx.h"
#include "logging.h"
-#include "trx_if.h"
#include "trxcon.h"
+#include "trx_if.h"
#include "l1ctl.h"
/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */
@@ -81,10 +84,49 @@ const uint8_t sched_nb_training_bits[8][26] = {
},
};
+/* Get a string representation of the burst buffer's completeness.
+ * Examples: " ****.." (incomplete, 4/6 bursts)
+ * " ****" (complete, all 4 bursts)
+ * "**.***.." (incomplete, 5/8 bursts) */
+const char *burst_mask2str(const uint8_t *mask, int bits)
+{
+ /* TODO: CSD is interleaved over 22 bursts, so the mask needs to be extended */
+ static char buf[8 + 1];
+ char *ptr = buf;
+
+ OSMO_ASSERT(bits <= 8 && bits > 0);
+
+ while (--bits >= 0)
+ *(ptr++) = (*mask & (1 << bits)) ? '*' : '.';
+ *ptr = '\0';
+
+ return buf;
+}
+
+int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn,
+ uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, size_t data_len)
+{
+ const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[lchan_type];
+
+ /* GSMTAP logging may not be enabled */
+ if (gsmtap == NULL)
+ return 0;
+
+ /* Omit frames with unknown channel type */
+ if (lchan_desc->gsmtap_chan_type == GSMTAP_CHANNEL_UNKNOWN)
+ return 0;
+
+ /* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */
+ return gsmtap_send(gsmtap, band_arfcn, tn, lchan_desc->gsmtap_chan_type,
+ lchan_desc->ss_nr, fn, signal_dbm, snr, data, data_len);
+}
+
int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
int bit_error_count, bool dec_failed, bool traffic)
{
+ const struct trx_meas_set *meas = &lchan->meas_avg;
const struct trx_lchan_desc *lchan_desc;
struct l1ctl_info_dl dl_hdr;
@@ -95,10 +137,14 @@ int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
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.rx_level = -(lchan->meas.rssi_sum / lchan->meas.rssi_num);
dl_hdr.num_biterr = bit_error_count;
+ /* sched_trx_meas_avg() gives us TDMA frame number of the first burst */
+ dl_hdr.frame_nr = htonl(meas->fn);
+
+ /* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */
+ dl_hdr.rx_level = dbm2rxlev(meas->rssi);
+
/* FIXME: set proper values */
dl_hdr.snr = 0;
@@ -108,6 +154,12 @@ int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
/* Put a packet to higher layers */
l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, traffic);
+ /* Optional GSMTAP logging */
+ if (l2_len > 0 && (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH)) {
+ sched_gsmtap_send(lchan->type, meas->fn, ts->index,
+ trx->band_arfcn, meas->rssi, 0, l2, l2_len);
+ }
+
return 0;
}
@@ -131,6 +183,14 @@ int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts,
l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, traffic);
+ /* Optional GSMTAP logging */
+ if (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH) {
+ sched_gsmtap_send(lchan->type, fn, ts->index,
+ trx->band_arfcn | ARFCN_UPLINK,
+ 0, 0, lchan->prim->payload,
+ lchan->prim->payload_len);
+ }
+
return 0;
}
diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c
index 93639a37..2c3c3b2e 100644
--- a/src/host/trxcon/sched_lchan_desc.c
+++ b/src/host/trxcon/sched_lchan_desc.c
@@ -23,295 +23,600 @@
*
*/
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/core/gsmtap.h>
+
#include "sched_trx.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);
+ const sbit_t *bits, const struct trx_meas_set *meas);
int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
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);
+ const sbit_t *bits, const struct trx_meas_set *meas);
int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
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 sbit_t *bits, const struct trx_meas_set *meas);
int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
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 sbit_t *bits, const struct trx_meas_set *meas);
int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
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 sbit_t *bits, const struct trx_meas_set *meas);
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 trx_lchan_desc[_TRX_CHAN_MAX] = {
- {
- TRXC_IDLE, "IDLE",
- 0x00, TRX_CH_LID_DEDIC,
- 0x00, 0x00,
-
- /**
- * MS: do nothing, save power...
- * BTS: send dummy burst on C0
- */
- NULL, NULL,
- },
- {
- TRXC_FCCH, "FCCH",
- 0x00, TRX_CH_LID_DEDIC,
- 0x00, 0x00,
-
- /* FCCH is handled by transceiver */
- NULL, NULL,
- },
- {
- TRXC_SCH, "SCH",
- 0x00, TRX_CH_LID_DEDIC,
- 0x00, TRX_CH_FLAG_AUTO,
-
- /**
- * We already have clock indications from TRX,
- * but we also need BSIC (BCC / NCC) value.
- */
- rx_sch_fn, NULL,
- },
- {
- TRXC_BCCH, "BCCH",
- 0x80, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO,
- rx_data_fn, NULL,
- },
- {
- TRXC_RACH, "RACH",
- 0x88, TRX_CH_LID_DEDIC,
- 0x00, TRX_CH_FLAG_AUTO,
- NULL, tx_rach_fn,
- },
- {
- TRXC_CCCH, "CCCH",
- 0x90, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO,
- rx_data_fn, NULL,
- },
- {
- TRXC_TCHF, "TCH/F",
- 0x08, TRX_CH_LID_DEDIC,
- 8 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_tchf_fn, tx_tchf_fn,
- },
- {
- TRXC_TCHH_0, "TCH/H(0)",
- 0x10, TRX_CH_LID_DEDIC,
- 6 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_tchh_fn, tx_tchh_fn,
- },
- {
- TRXC_TCHH_1, "TCH/H(1)",
- 0x18, TRX_CH_LID_DEDIC,
- 6 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_tchh_fn, tx_tchh_fn,
- },
- {
- TRXC_SDCCH4_0, "SDCCH/4(0)",
- 0x20, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH4_1, "SDCCH/4(1)",
- 0x28, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH4_2, "SDCCH/4(2)",
- 0x30, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH4_3, "SDCCH/4(3)",
- 0x38, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_0, "SDCCH/8(0)",
- 0x40, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_1, "SDCCH/8(1)",
- 0x48, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_2, "SDCCH/8(2)",
- 0x50, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_3, "SDCCH/8(3)",
- 0x58, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_4, "SDCCH/8(4)",
- 0x60, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_5, "SDCCH/8(5)",
- 0x68, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_6, "SDCCH/8(6)",
- 0x70, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SDCCH8_7, "SDCCH/8(7)",
- 0x78, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCHTF, "SACCH/TF",
- 0x08, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCHTH_0, "SACCH/TH(0)",
- 0x10, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCHTH_1, "SACCH/TH(1)",
- 0x18, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH4_0, "SACCH/4(0)",
- 0x20, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH4_1, "SACCH/4(1)",
- 0x28, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH4_2, "SACCH/4(2)",
- 0x30, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH4_3, "SACCH/4(3)",
- 0x38, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_0, "SACCH/8(0)",
- 0x40, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_1, "SACCH/8(1)",
- 0x48, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_2, "SACCH/8(2)",
- 0x50, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_3, "SACCH/8(3)",
- 0x58, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_4, "SACCH/8(4)",
- 0x60, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_5, "SACCH/8(5)",
- 0x68, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_6, "SACCH/8(6)",
- 0x70, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_SACCH8_7, "SACCH/8(7)",
- 0x78, TRX_CH_LID_SACCH,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX,
- rx_data_fn, tx_data_fn,
- },
- {
- TRXC_PDTCH, "PDTCH",
- 0xc0, TRX_CH_LID_DEDIC,
- 12 * GSM_BURST_PL_LEN, TRX_CH_FLAG_PDCH,
- rx_pdtch_fn, tx_pdtch_fn,
- },
- {
- TRXC_PTCCH, "PTCCH",
- 0xc0, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_PDCH,
- rx_data_fn, tx_data_fn,
+ [TRXC_IDLE] = {
+ .name = "IDLE",
+ .desc = "Idle channel",
+ /* The MS needs to perform neighbour measurements during
+ * IDLE slots, however this is not implemented (yet). */
+ },
+ [TRXC_FCCH] = {
+ .name = "FCCH", /* 3GPP TS 05.02, section 3.3.2.1 */
+ .desc = "Frequency correction channel",
+ /* Handled by transceiver, nothing to do. */
+ },
+ [TRXC_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,
+ .rx_fn = rx_sch_fn,
+ },
+ [TRXC_BCCH] = {
+ .name = "BCCH", /* 3GPP TS 05.02, section 3.3.2.3 */
+ .desc = "Broadcast control channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_BCCH,
+ .chan_nr = RSL_CHAN_BCCH,
+
+ /* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
+ * regular interleaving (3GPP TS 05.02, clause 7, table 3):
+ * a L2 frame is interleaved over 4 consecutive bursts. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_AUTO,
+ .rx_fn = rx_data_fn,
+ },
+ [TRXC_RACH] = {
+ .name = "RACH", /* 3GPP TS 05.02, section 3.3.3.1 */
+ .desc = "Random access channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_RACH,
+ .chan_nr = RSL_CHAN_RACH,
+
+ /* Tx only, RACH convolutional coding (3GPP TS 05.03, section 4.6). */
+ .flags = TRX_CH_FLAG_AUTO,
+ .tx_fn = tx_rach_fn,
+ },
+ [TRXC_CCCH] = {
+ .name = "CCCH", /* 3GPP TS 05.02, section 3.3.3.1 */
+ .desc = "Common control channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_CCCH,
+ .chan_nr = RSL_CHAN_PCH_AGCH,
+
+ /* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
+ * regular interleaving (3GPP TS 05.02, clause 7, table 3):
+ * a L2 frame is interleaved over 4 consecutive bursts. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_AUTO,
+ .rx_fn = rx_data_fn,
+ },
+ [TRXC_TCHF] = {
+ .name = "TCH/F", /* 3GPP TS 05.02, section 3.2 */
+ .desc = "Full Rate traffic channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F,
+ .chan_nr = RSL_CHAN_Bm_ACCHs,
+ .link_id = TRX_CH_LID_DEDIC,
+
+ /* 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 8 consecutive bursts
+ * using the even numbered bits of the first 4 bursts
+ * and odd numbered bits of the last 4 bursts;
+ * - a FACCH/F frame 'steals' (replaces) one traffic frame,
+ * interleaving is done in the same way.
+ *
+ * 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,
+ .rx_fn = rx_tchf_fn,
+ .tx_fn = tx_tchf_fn,
+ },
+ [TRXC_TCHH_0] = {
+ .name = "TCH/H(0)", /* 3GPP TS 05.02, section 3.2 */
+ .desc = "Half Rate traffic channel (sub-channel 0)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H,
+ .chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 0,
+
+ /* 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 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;
+ * - a FACCH/H frame 'steals' (replaces) two traffic frames,
+ * interleaving is done over 4 consecutive bursts,
+ * the same as given for a TCH/FS.
+ *
+ * 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,
+ .rx_fn = rx_tchh_fn,
+ .tx_fn = tx_tchh_fn,
+ },
+ [TRXC_TCHH_1] = {
+ .name = "TCH/H(1)", /* 3GPP TS 05.02, section 3.2 */
+ .desc = "Half Rate traffic channel (sub-channel 1)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H,
+ .chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 1,
+
+ /* Same as for TRXC_TCHH_0, see above. */
+ .burst_buf_size = 6 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_tchh_fn,
+ .tx_fn = tx_tchh_fn,
+ },
+ [TRXC_SDCCH4_0] = {
+ .name = "SDCCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 0)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 0,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH4_1] = {
+ .name = "SDCCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 1)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 1,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH4_2] = {
+ .name = "SDCCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 2)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 2,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH4_3] = {
+ .name = "SDCCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 3)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 3,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_0] = {
+ .name = "SDCCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 0)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 0,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_1] = {
+ .name = "SDCCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 1)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 1,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_2] = {
+ .name = "SDCCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 2)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 2,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_3] = {
+ .name = "SDCCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 3)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 3,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_4] = {
+ .name = "SDCCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 4)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 4,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_5] = {
+ .name = "SDCCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 5)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 5,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_6] = {
+ .name = "SDCCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 6)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 6,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SDCCH8_7] = {
+ .name = "SDCCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Stand-alone dedicated control channel (sub-channel 7)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
+ .link_id = TRX_CH_LID_DEDIC,
+ .ss_nr = 7,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCHTF] = {
+ .name = "SACCH/TF", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow TCH/F associated control channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_Bm_ACCHs,
+ .link_id = TRX_CH_LID_SACCH,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCHTH_0] = {
+ .name = "SACCH/TH(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow TCH/H associated control channel (sub-channel 0)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 0,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCHTH_1] = {
+ .name = "SACCH/TH(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow TCH/H associated control channel (sub-channel 1)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 1,
+
+ /* Same as for TRXC_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH4_0] = {
+ .name = "SACCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/4 associated control channel (sub-channel 0)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 0,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH4_1] = {
+ .name = "SACCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/4 associated control channel (sub-channel 1)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 1,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH4_2] = {
+ .name = "SACCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/4 associated control channel (sub-channel 2)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 2,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH4_3] = {
+ .name = "SACCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/4 associated control channel (sub-channel 3)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 3,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_0] = {
+ .name = "SACCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 0)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 0,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_1] = {
+ .name = "SACCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 1)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 1,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_2] = {
+ .name = "SACCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 2)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 2,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_3] = {
+ .name = "SACCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 3)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 3,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_4] = {
+ .name = "SACCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 4)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 4,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_5] = {
+ .name = "SACCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 5)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 5,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_6] = {
+ .name = "SACCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 6)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 6,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_SACCH8_7] = {
+ .name = "SACCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
+ .desc = "Slow SDCCH/8 associated control channel (sub-channel 7)",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH,
+ .chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
+ .link_id = TRX_CH_LID_SACCH,
+ .ss_nr = 7,
+
+ /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_CBTX,
+ .rx_fn = rx_data_fn,
+ .tx_fn = tx_data_fn,
+ },
+ [TRXC_PDTCH] = {
+ .name = "PDTCH", /* 3GPP TS 05.02, sections 3.2.4, 3.3.2.4 */
+ .desc = "Packet data traffic & control channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_PDTCH,
+ .chan_nr = RSL_CHAN_OSMO_PDCH,
+
+ /* Rx and Tx, multiple coding schemes: CS-1..4 and MCS-1..9 (3GPP TS
+ * 05.03, chapter 5), regular interleaving as specified for xCCH.
+ * NOTE: the burst buffer is three times bigger because the
+ * payload of EDGE bursts is three times longer. */
+ .burst_buf_size = 3 * 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_PDCH,
+ .rx_fn = rx_pdtch_fn,
+ .tx_fn = tx_pdtch_fn,
+ },
+ [TRXC_PTCCH] = {
+ .name = "PTCCH", /* 3GPP TS 05.02, section 3.3.4.2 */
+ .desc = "Packet Timing advance control channel",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_PTCCH,
+ .chan_nr = RSL_CHAN_OSMO_PDCH,
+ .link_id = TRX_CH_LID_PTCCH,
+
+ /* 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,
+ .rx_fn = rx_pdtch_fn,
+ .tx_fn = tx_rach_fn,
},
[TRXC_SDCCH4_CBCH] = {
- TRXC_SDCCH4_CBCH, "SDCCH/4(CBCH)",
- 0xc0, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO,
- rx_data_fn, NULL,
+ .name = "SDCCH/4(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
+ .desc = "Cell Broadcast channel on SDCCH/4",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH51,
+ .chan_nr = RSL_CHAN_OSMO_CBCH4,
+ .ss_nr = 2,
+
+ /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .flags = TRX_CH_FLAG_AUTO,
+ .rx_fn = rx_data_fn,
},
[TRXC_SDCCH8_CBCH] = {
- TRXC_SDCCH8_CBCH, "SDCCH/8(CBCH)",
- 0xc8, TRX_CH_LID_DEDIC,
- 4 * GSM_BURST_PL_LEN, 0x00,
- rx_data_fn, NULL,
+ .name = "SDCCH/8(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
+ .desc = "Cell Broadcast channel on SDCCH/8",
+ .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH52,
+ .chan_nr = RSL_CHAN_OSMO_CBCH8,
+ .ss_nr = 2,
+
+ /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */
+ .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ .rx_fn = rx_data_fn,
},
};
diff --git a/src/host/trxcon/sched_lchan_pdtch.c b/src/host/trxcon/sched_lchan_pdtch.c
index 1a8987b3..6a684897 100644
--- a/src/host/trxcon/sched_lchan_pdtch.c
+++ b/src/host/trxcon/sched_lchan_pdtch.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -38,46 +38,35 @@
#include "sched_trx.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.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 sbit_t *bits, const struct trx_meas_set *meas)
{
const struct trx_lchan_desc *lchan_desc;
uint8_t l2[GPRS_L2_MAX_LEN], *mask;
int n_errors, n_bits_total, rc;
sbit_t *buffer, *offset;
- 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;
- }
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bid != 0)
+ return 0;
/* Update mask */
*mask |= (1 << bid);
- /* Update measurements */
- lchan->meas.toa256_sum += toa256;
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_num++;
- lchan->meas.rssi_num++;
+ /* Store the measurements */
+ sched_trx_meas_push(lchan, meas);
/* Copy burst to buffer of 4 bursts */
offset = buffer + bid * 116;
@@ -88,24 +77,30 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
if (bid != 3)
return 0;
+ /* Calculate AVG of the measurements */
+ sched_trx_meas_avg(lchan, 4);
+
/* Check for complete set of bursts */
if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
+ LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
+ "fn=%u (%u/%u) for %s\n",
+ burst_mask2str(mask, 4), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % ts->mf_layout->period,
ts->mf_layout->period,
lchan_desc->name);
-
- return -1;
+ /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
}
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
/* Attempt to decode */
rc = gsm0503_pdtch_decode(l2, buffer,
NULL, &n_errors, &n_bits_total);
if (rc < 0) {
LOGP(DSCHD, LOGL_ERROR, "Received bad packet data frame "
- "at fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
+ "at fn=%u (%u/%u) for %s\n", lchan->meas_avg.fn,
+ lchan->meas_avg.fn % ts->mf_layout->period,
ts->mf_layout->period,
lchan_desc->name);
}
@@ -147,8 +142,10 @@ int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
/* Encode payload */
rc = gsm0503_pdtch_encode(buffer, lchan->prim->payload,
lchan->prim->payload_len);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n");
+ 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);
diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c
index a255efb8..fe5821ba 100644
--- a/src/host/trxcon/sched_lchan_rach.c
+++ b/src/host/trxcon/sched_lchan_rach.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -38,71 +38,129 @@
#include "sched_trx.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.h"
#include "l1ctl.h"
-/**
- * 8-bit RACH extended tail bits
- * GSM 05.02 Chapter 5.2.7 Access burst (AB)
- */
+/* 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
-static ubit_t rach_ext_tail_bits[] = {
+/* Extended tail bits (BN0..BN7) */
+static const ubit_t rach_ext_tail_bits[] = {
0, 0, 1, 1, 1, 0, 1, 0,
};
-/**
- * 41-bit RACH synchronization sequence
- * GSM 05.02 Chapter 5.2.7 Access burst (AB)
- */
-static ubit_t rach_synch_seq[] = {
- 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1,
- 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0,
- 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 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_rach_req *req;
+ 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 rc;
-
- /* Check the prim payload length */
- if (lchan->prim->payload_len != sizeof(*req)) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %zu), "
- "so dropping...\n", lchan->prim->payload_len, sizeof(*req));
-
+ 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;
}
- /* Get the payload from a current primitive */
- req = (struct l1ctl_rach_req *) lchan->prim->payload;
- /* Delay RACH sending according to offset value */
- if (req->offset-- > 0)
- return 0;
+ /* BN0-7: extended tail bits */
+ memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
+ burst_ptr += RACH_EXT_TAIL_BITS_LEN;
- /* Encode (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\n");
+ /* 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';
- /* Forget this primitive */
- sched_prim_drop(lchan);
+ /* BN49-84: encrypted bits (the payload) */
+ memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
+ burst_ptr += RACH_PAYLOAD_LEN;
- return rc;
- }
-
- /* Compose RACH burst */
- memcpy(burst, rach_ext_tail_bits, 8); /* TB */
- memcpy(burst + 8, rach_synch_seq, 41); /* sync seq */
- memcpy(burst + 49, payload, 36); /* payload */
- memset(burst + 85, 0, 63); /* TB + GP */
+ /* BN85-156: tail bits & extended guard period */
+ memset(burst_ptr, 0, burst + GSM_BURST_LEN - burst_ptr);
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn);
+ 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);
@@ -114,7 +172,13 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
}
/* Confirm RACH request */
- l1ctl_tx_rach_conf(trx->l1l, fn);
+ l1ctl_tx_rach_conf(trx->l1l, trx->band_arfcn, fn);
+
+ /* Optional GSMTAP logging */
+ sched_gsmtap_send(lchan->type, fn, ts->index,
+ trx->band_arfcn | ARFCN_UPLINK, 0, 0,
+ PRIM_IS_RACH11(lchan->prim) ? (uint8_t *) &ext_req->ra11 : &req->ra,
+ PRIM_IS_RACH11(lchan->prim) ? 2 : 1);
/* Forget processed primitive */
sched_prim_drop(lchan);
diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c
index 1b241a08..18d4c58d 100644
--- a/src/host/trxcon/sched_lchan_sch.c
+++ b/src/host/trxcon/sched_lchan_sch.c
@@ -40,7 +40,6 @@
#include "sched_trx.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.h"
#include "l1ctl.h"
static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
@@ -48,7 +47,7 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
uint8_t t3p;
uint32_t sb;
- sb = (sb_info[3] << 24)
+ sb = ((uint32_t)sb_info[3] << 24)
| (sb_info[2] << 16)
| (sb_info[1] << 8)
| sb_info[0];
@@ -71,7 +70,7 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
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)
+ const sbit_t *bits, const struct trx_meas_set *meas)
{
sbit_t payload[2 * 39];
struct gsm_time time;
@@ -118,7 +117,7 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
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;
+ data->rx_level = -(meas->rssi);
/* FIXME: set proper values */
data->num_biterr = 0;
diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c
index 09d504f8..c5362f0b 100644
--- a/src/host/trxcon/sched_lchan_tchf.c
+++ b/src/host/trxcon/sched_lchan_tchf.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -40,46 +40,35 @@
#include "sched_trx.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.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 sbit_t *bits, const struct trx_meas_set *meas)
{
const struct trx_lchan_desc *lchan_desc;
int n_errors = -1, n_bits_total, rc;
sbit_t *buffer, *offset;
uint8_t l2[128], *mask;
- 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;
- }
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bid != 0)
+ return 0;
/* Update mask */
*mask |= (1 << bid);
- /* Update mask and RSSI */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.rssi_num++;
- lchan->meas.toa256_num++;
+ /* Store the measurements */
+ sched_trx_meas_push(lchan, meas);
/* Copy burst to end of buffer of 8 bursts */
offset = buffer + bid * 116 + 464;
@@ -90,18 +79,24 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
if (bid != 3)
return 0;
+ /* Calculate AVG of the measurements */
+ sched_trx_meas_avg(lchan, 8);
+
/* 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,
+ if ((*mask & 0xff) != 0xff) {
+ LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) traffic frame at "
+ "fn=%u (%u/%u) for %s\n",
+ burst_mask2str(mask, 8), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % ts->mf_layout->period,
ts->mf_layout->period,
lchan_desc->name);
+ /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
- /* Send BFI */
- goto bfi;
}
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
switch (lchan->tch_mode) {
case GSM48_CMODE_SIGN:
case GSM48_CMODE_SPEECH_V1: /* FR */
@@ -139,7 +134,8 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
n_errors, false, false);
- /* Send BFI instead of stolen TCH frame */
+ /* Send BFI substituting a stolen TCH frame */
+ n_errors = -1; /* ensure fake measurements */
goto bfi;
} else {
/* A good TCH frame received */
@@ -151,9 +147,17 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
n_errors, false, true);
bfi:
- /* Didn't try to decode */
- if (n_errors < 0)
- n_errors = 116 * 4;
+ /* Didn't try to decode, fake measurements */
+ if (n_errors < 0) {
+ lchan->meas_avg = (struct trx_meas_set) {
+ .fn = lchan->meas_avg.fn,
+ .toa256 = 0,
+ .rssi = -110,
+ };
+
+ /* No bursts => no errors */
+ n_errors = 0;
+ }
/* BFI is not applicable in signalling mode */
if (lchan->tch_mode == GSM48_CMODE_SIGN)
@@ -241,7 +245,9 @@ int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
/* 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\n");
+ 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);
diff --git a/src/host/trxcon/sched_lchan_tchh.c b/src/host/trxcon/sched_lchan_tchh.c
index 7fb2809d..b6f07082 100644
--- a/src/host/trxcon/sched_lchan_tchh.c
+++ b/src/host/trxcon/sched_lchan_tchh.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
@@ -43,7 +43,6 @@
#include "sched_trx.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.h"
#include "l1ctl.h"
static const uint8_t tch_h0_traffic_block_map[3][4] = {
@@ -172,8 +171,8 @@ uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
#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); \
+ fn_diff = GSM_TDMA_FN_DIFF(fn_mf, map[i][0]); \
+ return GSM_TDMA_FN_SUB(last_fn, fn_diff); \
} \
} while (++i < ARRAY_SIZE(map))
@@ -201,7 +200,7 @@ uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
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 sbit_t *bits, const struct trx_meas_set *meas)
{
const struct trx_lchan_desc *lchan_desc;
int n_errors = -1, n_bits_total, rc;
@@ -235,17 +234,8 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
/* 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.rssi_num++;
- lchan->meas.toa256_num++;
+ /* Store the measurements */
+ sched_trx_meas_push(lchan, meas);
/* Copy burst to the end of buffer of 6 bursts */
offset = buffer + bid * 116 + 464;
@@ -301,8 +291,12 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
/* 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);
+ /* Calculate AVG of the measurements (assuming 4 bursts) */
+ sched_trx_meas_avg(lchan, 4);
+
+ LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame (%s) "
+ "at fn=%u on %s (rc=%d)\n", burst_mask2str(mask, 6),
+ lchan->meas_avg.fn, lchan_desc->name, rc);
/* Send BFI */
goto bfi;
@@ -310,24 +304,23 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
/* 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 */
+ /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */
+ sched_trx_meas_avg(lchan, 6);
/* FACCH/H received, forward to the higher layers */
sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
n_errors, false, false);
- /* 1/2 BFI */
+ /* Send BFI substituting 1/2 stolen TCH frames */
+ n_errors = -1; /* ensure fake measurements */
goto bfi;
} else {
/* A good TCH frame received */
l2_len = rc;
- }
- /* Calculate TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, false); /* TCH/H */
+ /* Calculate AVG of the measurements (traffic takes 4 bursts) */
+ sched_trx_meas_avg(lchan, 4);
+ }
/* Send a traffic frame to the higher layers */
return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
@@ -342,13 +335,17 @@ bfi_shift:
*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 */
+ /* Didn't try to decode, fake measurements */
+ if (n_errors < 0) {
+ lchan->meas_avg = (struct trx_meas_set) {
+ .fn = sched_tchh_block_dl_first_fn(lchan->type, fn, false),
+ .toa256 = 0,
+ .rssi = -110,
+ };
+
+ /* No bursts => no errors */
+ n_errors = 0;
+ }
/* BFI is not applicable in signalling mode */
if (lchan->tch_mode == GSM48_CMODE_SIGN)
@@ -447,7 +444,9 @@ int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
/* 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\n");
+ 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);
diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c
index b31f727a..a0b61adc 100644
--- a/src/host/trxcon/sched_lchan_xcch.c
+++ b/src/host/trxcon/sched_lchan_xcch.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -38,45 +38,34 @@
#include "sched_trx.h"
#include "logging.h"
#include "trx_if.h"
-#include "trxcon.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 sbit_t *bits, const struct trx_meas_set *meas)
{
const struct trx_lchan_desc *lchan_desc;
uint8_t l2[GSM_MACBLOCK_LEN], *mask;
int n_errors, n_bits_total, rc;
sbit_t *buffer, *offset;
- 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;
- }
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bid != 0)
+ return 0;
/* Update mask */
*mask |= (1 << bid);
- /* Update measurements */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.rssi_num++;
- lchan->meas.toa256_num++;
+ /* Store the measurements */
+ sched_trx_meas_push(lchan, meas);
/* Copy burst to buffer of 4 bursts */
offset = buffer + bid * 116;
@@ -87,21 +76,32 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
if (bid != 3)
return 0;
+ /* Calculate AVG of the measurements */
+ sched_trx_meas_avg(lchan, 4);
+
/* Check for complete set of bursts */
if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
+ LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
+ "fn=%u (%u/%u) for %s\n",
+ burst_mask2str(mask, 4), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % ts->mf_layout->period,
ts->mf_layout->period,
lchan_desc->name);
+ /* NOTE: xCCH has an insane amount of redundancy for error
+ * correction, so even just 2 valid bursts might be enough
+ * to reconstruct some L2 frames. This is why we do not
+ * abort here. */
}
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
/* Attempt to decode */
rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total);
if (rc) {
LOGP(DSCHD, LOGL_ERROR, "Received bad data frame at fn=%u "
- "(%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
+ "(%u/%u) for %s\n", lchan->meas_avg.fn,
+ lchan->meas_avg.fn % ts->mf_layout->period,
ts->mf_layout->period,
lchan_desc->name);
@@ -153,7 +153,9 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
/* Encode payload */
rc = gsm0503_xcch_encode(buffer, lchan->prim->payload);
if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n");
+ 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);
@@ -195,14 +197,14 @@ send_burst:
/* If we have sent the last (4/4) burst */
if ((*mask & 0x0f) == 0x0f) {
+ /* Confirm data sending */
+ sched_send_dt_conf(trx, ts, lchan, fn, false);
+
/* 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
index 0dcf3e7e..9b759af3 100644
--- a/src/host/trxcon/sched_mframe.c
+++ b/src/host/trxcon/sched_mframe.c
@@ -292,10 +292,10 @@ static const struct trx_frame frame_bcch_sdcch4_cbch[102] = {
{ 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_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 },
+ { 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] = {
@@ -1916,6 +1916,50 @@ static const struct trx_frame frame_pdch[104] = {
{ 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:
*
@@ -1928,97 +1972,116 @@ static const struct trx_frame frame_pdch[104] = {
static const struct trx_multiframe layouts[] = {
{
GSM_PCHAN_NONE, "NONE",
- 0, 0xff, (uint64_t) 0x00,
+ 0, 0xff,
+ 0x00,
NULL
},
{
GSM_PCHAN_CCCH, "BCCH+CCCH",
- 51, 0xff, (uint64_t) 0x3e,
+ 51, 0xff,
+ M64_BCCH_CCCH,
frame_bcch
},
{
GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4",
- 102, 0xff, (uint64_t) 0xf001e3e,
+ 102, 0xff,
+ M64_BCCH_CCCH | M64_SDCCH4,
frame_bcch_sdcch4
},
{
GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH",
- 102, 0xff, (uint64_t) 0x400f001e3e,
+ 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, (uint64_t) 0xff01fe000,
+ 102, 0xff,
+ M64_SDCCH8,
frame_sdcch8
},
{
GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH",
- 102, 0xff, (uint64_t) 0x8ff01fe000,
+ 102, 0xff,
+ M64_SDCCH8 | M64(TRXC_SDCCH8_CBCH),
frame_sdcch8_cbch
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x01, (uint64_t) 0x200040,
+ 104, 0x01,
+ M64_TCHF,
frame_tchf_ts0
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x02, (uint64_t) 0x200040,
+ 104, 0x02,
+ M64_TCHF,
frame_tchf_ts1
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x04, (uint64_t) 0x200040,
+ 104, 0x04,
+ M64_TCHF,
frame_tchf_ts2
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x08, (uint64_t) 0x200040,
+ 104, 0x08,
+ M64_TCHF,
frame_tchf_ts3
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x10, (uint64_t) 0x200040,
+ 104, 0x10,
+ M64_TCHF,
frame_tchf_ts4
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x20, (uint64_t) 0x200040,
+ 104, 0x20,
+ M64_TCHF,
frame_tchf_ts5
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x40, (uint64_t) 0x200040,
+ 104, 0x40,
+ M64_TCHF,
frame_tchf_ts6
},
{
GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x80, (uint64_t) 0x200040,
+ 104, 0x80,
+ M64_TCHF,
frame_tchf_ts7
},
{
GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x03, (uint64_t) 0xc00180,
+ 104, 0x03,
+ M64_TCHH,
frame_tchh_ts01
},
{
GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x0c, (uint64_t) 0xc00180,
+ 104, 0x0c,
+ M64_TCHH,
frame_tchh_ts23
},
{
GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x30, (uint64_t) 0xc00180,
+ 104, 0x30,
+ M64_TCHH,
frame_tchh_ts45
},
{
GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0xc0, (uint64_t) 0xc00180,
+ 104, 0xc0,
+ M64_TCHH,
frame_tchh_ts67
},
{
GSM_PCHAN_PDCH, "PDCH",
- 104, 0xff, (uint64_t) 0x3000000000,
+ 104, 0xff,
+ M64(TRXC_PDTCH) | M64(TRXC_PTCCH),
frame_pdch
},
};
diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c
index 275a0509..94733203 100644
--- a/src/host/trxcon/sched_prim.c
+++ b/src/host/trxcon/sched_prim.c
@@ -144,10 +144,23 @@ static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan)
/* LAPDm header */
0x01, 0x03, 0x49,
- /* Measurement report */
- 0x06, 0x15, 0x36, 0x36, 0x01, 0xC0,
-
- /* TODO: Padding? Randomize if so */
+ /* 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,
};
@@ -171,25 +184,18 @@ static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan)
/* 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;
+ prim->payload[0] = lchan->ts->trx->tx_power;
+ prim->payload[1] = lchan->ts->trx->ta;
}
-#endif
/* Inform about the cache usage count */
if (cached && lchan->sacch.mr_cache_usage > 5) {
@@ -253,10 +259,6 @@ static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue,
if (prim->chan != lchan->type)
continue;
- /* Just to be sure... */
- if (prim->payload_len != GSM_MACBLOCK_LEN)
- continue;
-
/* Look for a Measurement Report */
if (!prim_mr && PRIM_IS_MR(prim))
prim_mr = prim;
@@ -307,6 +309,10 @@ static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue,
/* 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;
}
diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c
index 19d1fe82..0025e0cc 100644
--- a/src/host/trxcon/sched_trx.c
+++ b/src/host/trxcon/sched_trx.c
@@ -2,7 +2,7 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: GSM PHY routines
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -26,13 +26,16 @@
#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"
@@ -65,8 +68,7 @@ static void sched_frame_clck_cb(struct trx_sched *sched)
* 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);
+ fn = GSM_TDMA_FN_SUM(sched->fn_counter_proc, sched->fn_counter_advance);
/* Get frame from multiframe */
offset = fn % ts->mf_layout->period;
@@ -114,6 +116,11 @@ static void sched_frame_clck_cb(struct trx_sched *sched)
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);
}
@@ -160,7 +167,7 @@ int sched_trx_shutdown(struct trx_instance *trx)
return 0;
}
-int sched_trx_reset(struct trx_instance *trx, int reset_clock)
+int sched_trx_reset(struct trx_instance *trx, bool reset_clock)
{
int i;
@@ -194,6 +201,9 @@ struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn)
/* Allocate a new one */
trx->ts_list[tn] = talloc_zero(trx, struct trx_ts);
+ /* Add backpointer */
+ trx->ts_list[tn]->trx = trx;
+
/* Assign TS index */
trx->ts_list[tn]->index = tn;
@@ -256,6 +266,8 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn,
/* Choose proper multiframe layout */
ts->mf_layout = sched_mframe_layout(config, tn);
+ if (!ts->mf_layout)
+ return -EINVAL;
if (ts->mf_layout->chan_config != config)
return -EINVAL;
@@ -277,6 +289,9 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn,
if (!lchan)
return -ENOMEM;
+ /* set backpointer */
+ lchan->ts = ts;
+
/* Set channel type */
lchan->type = type;
@@ -305,9 +320,6 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn)
if (ts == NULL)
return -EINVAL;
- /* Flush TS frame counter */
- ts->mf_last_fn = 0;
-
/* Undefine multiframe layout */
ts->mf_layout = NULL;
@@ -445,10 +457,19 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan)
/* Prevent NULL-pointer deference */
OSMO_ASSERT(lchan != NULL);
+ /* Print some TDMA statistics for Downlink */
+ if (trx_lchan_desc[lchan->type].rx_fn && lchan->active) {
+ LOGP(DSCH, LOGL_DEBUG, "TDMA statistics for lchan=%s on ts=%u: "
+ "%lu DL frames have been processed, "
+ "%lu lost (compensated), last fn=%u\n",
+ trx_lchan_desc[lchan->type].name, lchan->ts->index,
+ lchan->tdma.num_proc, lchan->tdma.num_lost,
+ lchan->tdma.last_proc);
+ }
+
/* Reset internal state variables */
lchan->rx_burst_mask = 0x00;
lchan->tx_burst_mask = 0x00;
- lchan->rx_first_fn = 0;
/* Free burst memory */
talloc_free(lchan->rx_bursts);
@@ -476,6 +497,9 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan)
/* Reset ciphering state */
memset(&lchan->a5, 0x00, sizeof(lchan->a5));
+
+ /* Reset TDMA frame statistics */
+ memset(&lchan->tdma, 0x00, sizeof(lchan->tdma));
}
int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan)
@@ -529,18 +553,20 @@ enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr)
{
uint8_t cbits = chan_nr >> 3;
- if (cbits == 0x01)
+ if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
return GSM_PCHAN_TCH_F;
- else if ((cbits & 0x1e) == 0x02)
+ else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0))
return GSM_PCHAN_TCH_H;
- else if ((cbits & 0x1c) == 0x04)
+ else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0))
return GSM_PCHAN_CCCH_SDCCH4;
- else if ((cbits & 0x1f) == 0x18)
- return GSM_PCHAN_CCCH_SDCCH4_CBCH;
- else if ((cbits & 0x18) == 0x08)
+ else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0))
return GSM_PCHAN_SDCCH8_SACCH8C;
- else if ((cbits & 0x1f) == 0x19)
+ 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;
}
@@ -593,9 +619,84 @@ static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan,
}
}
+static int subst_frame_loss(struct trx_lchan_state *lchan,
+ trx_lchan_rx_func *handler,
+ uint32_t fn)
+{
+ const struct trx_multiframe *mf;
+ const struct trx_frame *fp;
+ int elapsed, i;
+
+ /* Wait until at least one TDMA frame is processed */
+ if (lchan->tdma.num_proc == 0)
+ return -EAGAIN;
+
+ /* Short alias for the current multiframe */
+ mf = lchan->ts->mf_layout;
+
+ /* Calculate how many frames elapsed since the last received one.
+ * The algorithm is based on GSM::FNDelta() from osmo-trx. */
+ elapsed = fn - lchan->tdma.last_proc;
+ if (elapsed >= GSM_TDMA_HYPERFRAME / 2)
+ elapsed -= GSM_TDMA_HYPERFRAME;
+ else if (elapsed < -GSM_TDMA_HYPERFRAME / 2)
+ elapsed += GSM_TDMA_HYPERFRAME;
+
+ /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */
+ if (elapsed < 0) {
+ /* This burst has already been substituted by a dummy burst (all bits set to zero),
+ * so better drop it. Otherwise we risk to get undefined behavior in handler(). */
+ LOGP(DSCHD, LOGL_ERROR, "(%s) Rx burst with fn=%u older than the last "
+ "processed fn=%u (see OS#4658) => dropping\n",
+ trx_lchan_desc[lchan->type].name,
+ fn, lchan->tdma.last_proc);
+ return -EALREADY;
+ }
+
+ /* Check how many frames we (potentially) need to compensate */
+ if (elapsed > mf->period) {
+ LOGP(DSCHD, LOGL_NOTICE, "Too many (>%u) contiguous TDMA frames elapsed (%d) "
+ "since the last processed fn=%u (current %u)\n",
+ mf->period, elapsed, lchan->tdma.last_proc, fn);
+ return -EIO;
+ } else if (elapsed == 0) {
+ LOGP(DSCHD, LOGL_ERROR, "No TDMA frames elapsed since the last processed "
+ "fn=%u, must be a bug?\n", lchan->tdma.last_proc);
+ return -EIO;
+ }
+
+ static const sbit_t bits[148] = { 0 };
+ struct trx_meas_set fake_meas = {
+ .fn = lchan->tdma.last_proc,
+ .rssi = -120,
+ .toa256 = 0,
+ };
+
+ /* Traverse from fp till the current frame */
+ for (i = 0; i < elapsed - 1; i++) {
+ fp = &mf->frames[GSM_TDMA_FN_INC(fake_meas.fn) % mf->period];
+ if (fp->dl_chan != lchan->type)
+ continue;
+
+ LOGP(DSCHD, LOGL_NOTICE, "Substituting lost TDMA frame %u on %s\n",
+ fake_meas.fn, trx_lchan_desc[lchan->type].name);
+
+ handler(lchan->ts->trx, lchan->ts, lchan,
+ fake_meas.fn, fp->dl_bid,
+ bits, &fake_meas);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = fake_meas.fn;
+ lchan->tdma.num_proc++;
+ lchan->tdma.num_lost++;
+ }
+
+ return 0;
+}
+
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
- int8_t rssi, int16_t toa256)
+ uint32_t fn, sbit_t *bits, uint16_t nbits,
+ const struct trx_meas_set *meas)
{
struct trx_lchan_state *lchan;
const struct trx_frame *frame;
@@ -603,8 +704,8 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
trx_lchan_rx_func *handler;
enum trx_lchan_type chan;
- uint32_t fn, elapsed;
uint8_t offset, bid;
+ int rc;
/* Check whether required timeslot is allocated and configured */
ts = trx->ts_list[tn];
@@ -614,62 +715,54 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t 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;
+ /* Get frame from multiframe */
+ offset = fn % ts->mf_layout->period;
+ frame = ts->mf_layout->frames + offset;
- /* Find required channel state */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- goto next_frame;
+ /* Get required info from frame */
+ bid = frame->dl_bid;
+ chan = frame->dl_chan;
+ handler = trx_lchan_desc[chan].rx_fn;
- /* Ensure that channel is active */
- if (!lchan->active)
- goto next_frame;
+ /* Omit bursts which have no handler, like IDLE bursts.
+ * TODO: handle noise indications during IDLE frames. */
+ if (!handler)
+ return -ENODEV;
- /* 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);
+ /* Find required channel state */
+ lchan = sched_trx_find_lchan(ts, chan);
+ if (lchan == NULL)
+ return -ENODEV;
- /* Put burst to handler */
- handler(trx, ts, lchan, fn, bid, bits, rssi, toa256);
- }
+ /* Ensure that channel is active */
+ if (!lchan->active)
+ return 0;
-next_frame:
- /* Reached current fn */
- if (fn == burst_fn)
- break;
+ /* Compensate lost TDMA frames (if any) */
+ rc = subst_frame_loss(lchan, handler, fn);
+ if (rc == -EALREADY)
+ return rc;
- fn = TDMA_FN_INC(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, meas);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = fn;
+
+ if (++lchan->tdma.num_proc == 0) {
+ /* Theoretically, we may have an integer overflow of num_proc counter.
+ * As a consequence, subst_frame_loss() will be unable to compensate
+ * one (potentionally lost) Downlink burst. On practice, it would
+ * happen once in 4615 * 10e-6 * (2 ^ 32 - 1) seconds or ~6 years. */
+ LOGP(DSCHD, LOGL_NOTICE, "Too many TDMA frames have been processed. "
+ "Are you running trxcon for more than 6 years?!?\n");
+ lchan->tdma.num_proc = 1;
}
- /* Set last processed frame number */
- ts->mf_last_fn = fn;
-
return 0;
}
@@ -692,3 +785,57 @@ int sched_trx_handle_tx_burst(struct trx_instance *trx,
return 0;
}
+
+#define MEAS_HIST_FIRST(hist) \
+ (&hist->buf[0])
+#define MEAS_HIST_LAST(hist) \
+ (MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1)
+
+/* Add a new set of measurements to the history */
+void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas)
+{
+ struct trx_lchan_meas_hist *hist = &lchan->meas_hist;
+
+ /* Find a new position where to store the measurements */
+ if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL)
+ hist->head = MEAS_HIST_FIRST(hist);
+ else
+ hist->head++;
+
+ *hist->head = *meas;
+}
+
+/* Calculate the AVG of n measurements from the history */
+void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n)
+{
+ struct trx_lchan_meas_hist *hist = &lchan->meas_hist;
+ struct trx_meas_set *meas = hist->head;
+ int toa256_sum = 0;
+ int rssi_sum = 0;
+ int i;
+
+ OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf));
+ OSMO_ASSERT(meas != NULL);
+
+ /* Traverse backwards up to n entries, calculate the sum */
+ for (i = 0; i < n; i++) {
+ toa256_sum += meas->toa256;
+ rssi_sum += meas->rssi;
+
+ /* Do not go below the first burst */
+ if (i + 1 == n)
+ break;
+
+ if (meas == MEAS_HIST_FIRST(hist))
+ meas = MEAS_HIST_LAST(hist);
+ else
+ meas--;
+ }
+
+ /* Calculate the AVG */
+ lchan->meas_avg.toa256 = toa256_sum / n;
+ lchan->meas_avg.rssi = rssi_sum / n;
+
+ /* As a bonus, store TDMA frame number of the first burst */
+ lchan->meas_avg.fn = meas->fn;
+}
diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h
index b7236d51..fb7ecd49 100644
--- a/src/host/trxcon/sched_trx.h
+++ b/src/host/trxcon/sched_trx.h
@@ -5,6 +5,7 @@
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/linuxlist.h>
@@ -23,6 +24,10 @@
#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 */
@@ -35,6 +40,7 @@
/* Forward declaration to avoid mutual include */
struct trx_lchan_state;
+struct trx_meas_set;
struct trx_instance;
struct trx_ts;
@@ -93,22 +99,27 @@ enum trx_lchan_type {
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);
+ uint32_t fn, uint8_t bid, const sbit_t *bits,
+ const struct trx_meas_set *meas);
typedef int trx_lchan_tx_func(struct trx_instance *trx,
struct trx_ts *ts, struct trx_lchan_state *lchan,
uint32_t fn, uint8_t bid);
struct trx_lchan_desc {
- /*! \brief TRX Channel Type */
- enum trx_lchan_type chan;
/*! \brief Human-readable name */
const char *name;
+ /*! \brief Human-readable description */
+ const char *desc;
+
/*! \brief Channel Number (like in RSL) */
uint8_t chan_nr;
/*! \brief Link ID (like in RSL) */
uint8_t link_id;
+ /*! \brief Sub-slot number (for SDCCH and TCH/H) */
+ uint8_t ss_nr;
+ /*! \brief GSMTAP channel type (see GSMTAP_CHANNEL_*) */
+ uint8_t gsmtap_chan_type;
/*! \brief How much memory do we need to store bursts */
size_t burst_buf_size;
@@ -147,6 +158,21 @@ struct trx_multiframe {
const struct trx_frame *frames;
};
+struct trx_meas_set {
+ /*! \brief TDMA frame number of the first burst this set belongs to */
+ uint32_t fn;
+ /*! \brief ToA256 (Timing of Arrival, 1/256 of a symbol) */
+ int16_t toa256;
+ /*! \brief RSSI (Received Signal Strength Indication) */
+ int8_t rssi;
+};
+
+/* Simple ring buffer (up to 8 unique measurements) */
+struct trx_lchan_meas_hist {
+ struct trx_meas_set buf[8];
+ struct trx_meas_set *head;
+};
+
/* States each channel on a multiframe */
struct trx_lchan_state {
/*! \brief Channel type */
@@ -158,8 +184,6 @@ struct trx_lchan_state {
/*! \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 */
@@ -180,16 +204,20 @@ struct trx_lchan_state {
/*! \brief pending FACCH/H blocks on Uplink */
uint8_t ul_facch_blocks;
+ /*! \brief Downlink measurements history */
+ struct trx_lchan_meas_hist meas_hist;
+ /*! \brief AVG measurements of the last received block */
+ struct trx_meas_set meas_avg;
+
+ /*! \brief TDMA loss detection state */
struct {
- /*! \brief Number of RSSI values */
- uint8_t rssi_num;
- /*! \brief Sum of RSSI values */
- float rssi_sum;
- /*! \brief Number of TOA values */
- uint8_t toa256_num;
- /*! \brief Sum of TOA values */
- int32_t toa256_sum;
- } meas;
+ /*! \brief Last processed TDMA frame number */
+ uint32_t last_proc;
+ /*! \brief Number of processed TDMA frames */
+ unsigned long num_proc;
+ /*! \brief Number of lost TDMA frames */
+ unsigned long num_lost;
+ } tdma;
/*! \brief SACCH state */
struct {
@@ -229,13 +257,14 @@ struct trx_lchan_state {
uint8_t key_len;
uint8_t algo;
} a5;
+
+ /* TS that this lchan belongs to */
+ struct trx_ts *ts;
};
struct trx_ts {
/*! \brief Timeslot index within a frame (0..7) */
uint8_t index;
- /*! \brief Last received frame number */
- uint32_t mf_last_fn;
/*! \brief Pointer to multiframe layout */
const struct trx_multiframe *mf_layout;
@@ -243,6 +272,8 @@ struct trx_ts {
struct llist_head lchans;
/*! \brief Queue primitives for TX */
struct llist_head tx_prims;
+ /* backpointer to its TRX */
+ struct trx_instance *trx;
};
/* Represents one TX primitive in the queue of trx_ts */
@@ -263,7 +294,7 @@ const struct trx_multiframe *sched_mframe_layout(
/* Scheduler management functions */
int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance);
-int sched_trx_reset(struct trx_instance *trx, int reset_clock);
+int sched_trx_reset(struct trx_instance *trx, bool reset_clock);
int sched_trx_shutdown(struct trx_instance *trx);
/* Timeslot management functions */
@@ -310,6 +341,16 @@ int sched_prim_push(struct trx_instance *trx,
#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)
@@ -323,8 +364,8 @@ 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);
+ uint32_t fn, sbit_t *bits, uint16_t nbits,
+ const struct trx_meas_set *meas);
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);
@@ -332,12 +373,16 @@ int sched_trx_handle_tx_burst(struct trx_instance *trx,
/* Shared declarations for lchan handlers */
extern const uint8_t sched_nb_training_bits[8][26];
+const char *burst_mask2str(const uint8_t *mask, int bits);
size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan);
int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
int bit_error_count, bool dec_failed, bool traffic);
int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts,
struct trx_lchan_state *lchan, uint32_t fn, bool traffic);
+int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn,
+ uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, size_t data_len);
/* Interleaved TCH/H block TDMA frame mapping */
uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
@@ -354,3 +399,7 @@ bool sched_tchh_block_map_fn(enum trx_lchan_type chan,
sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
#define sched_tchh_facch_end(chan, fn, ul) \
sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
+
+/* Measurement history */
+void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas);
+void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n);
diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h
index 7ab17ab5..43127cc1 100644
--- a/src/host/trxcon/scheduler.h
+++ b/src/host/trxcon/scheduler.h
@@ -4,23 +4,7 @@
#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))
+#include <osmocom/gsm/gsm0502.h>
enum tdma_sched_clck_state {
SCH_CLCK_STATE_WAIT,
@@ -33,14 +17,14 @@ struct trx_sched;
/*! \brief One scheduler instance */
struct trx_sched {
/*! \brief Clock state */
- uint8_t state;
+ enum tdma_sched_clck_state state;
/*! \brief Local clock source */
struct timespec clock;
/*! \brief Count of processed frames */
uint32_t fn_counter_proc;
/*! \brief Local frame counter advance */
uint32_t fn_counter_advance;
- /*! \brief Frame counter */
+ /*! \brief Count of lost frames */
uint32_t fn_counter_lost;
/*! \brief Frame callback timer */
struct osmo_timer_list clock_timer;
diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c
index b8bbace5..e1ce5881 100644
--- a/src/host/trxcon/trx_if.c
+++ b/src/host/trxcon/trx_if.c
@@ -254,6 +254,11 @@ int trx_if_cmd_poweroff(struct trx_instance *trx)
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");
+ return -EAGAIN;
+ }
return trx_ctrl_cmd(trx, 1, "POWERON", "");
}
@@ -391,18 +396,81 @@ int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta)
return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta);
}
+/*
+ * Frequency Hopping parameters indication.
+ *
+ * SETFH instructs transceiver to enable frequency hopping mode
+ * using the given HSN, MAIO, and Mobile Allocation parameters.
+ *
+ * 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)
+{
+ /* 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;
+
+ /* Make sure that Mobile Allocation has at least one ARFCN */
+ if (!ma_len || ma == NULL) {
+ LOGP(DTRX, LOGL_ERROR, "Mobile Allocation is empty?!?\n");
+ return -EINVAL;
+ }
+
+ /* Compose a sequence of Rx/Tx frequencies (mobile allocation) */
+ for (i = 0, ptr = ma_buf; i < ma_len; i++) {
+ /* Convert ARFCN to a pair of Rx/Tx frequencies (Hz * 10) */
+ rx_freq = gsm_arfcn2freq10(ma[i], 0); /* Rx: Downlink */
+ tx_freq = gsm_arfcn2freq10(ma[i], 1); /* Tx: Uplink */
+ if (rx_freq == 0xffff || tx_freq == 0xffff) {
+ LOGP(DTRX, LOGL_ERROR, "Failed to convert ARFCN %u "
+ "to a pair of Rx/Tx frequencies\n",
+ ma[i] & ~ARFCN_FLAG_MASK);
+ return -EINVAL;
+ }
+
+ /* Append a pair of Rx/Tx frequencies (in kHz) to the buffer */
+ rc = snprintf(ptr, ma_buf_len, "%u %u ", rx_freq * 100, tx_freq * 100);
+ if (rc < 0 || rc > ma_buf_len) { /* Prevent buffer overflow */
+ LOGP(DTRX, LOGL_ERROR, "Not enough room to encode "
+ "Mobile Allocation (N=%zu)\n", ma_len);
+ return -ENOSPC;
+ }
+
+ /* Move pointer */
+ ma_buf_len -= rc;
+ ptr += rc;
+ }
+
+ /* Overwrite the last space */
+ *(ptr - 1) = '\0';
+
+ return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", hsn, maio, ma_buf);
+}
+
/* Get response from CTRL socket */
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 len, resp, rsp_len;
- char buf[1500], *p;
-
- len = recv(ofd->fd, buf, sizeof(buf) - 1, 0);
- if (len <= 0)
- return len;
- buf[len] = '\0';
+ int resp, rsp_len;
+ 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);
+ return read_len;
+ }
+ buf[read_len] = '\0';
if (!!strncmp(buf, "RSP ", 4)) {
LOGP(DTRX, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf);
@@ -448,10 +516,14 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
}
/* Trigger state machine */
- if (!strncmp(tcm->cmd + 4, "POWERON", 7))
+ if (!strncmp(tcm->cmd + 4, "POWERON", 7)) {
+ trx->powered_up = true;
osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_ACTIVE, 0, 0);
- else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8))
+ }
+ else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) {
+ trx->powered_up = false;
osmo_fsm_inst_state_chg(trx->fsm, 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))
@@ -499,25 +571,28 @@ 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];
+ struct trx_meas_set meas;
+ uint8_t buf[TRXD_BUF_SIZE];
sbit_t bits[148];
int8_t rssi, tn;
int16_t toa256;
uint32_t fn;
- int len;
+ ssize_t read_len;
- len = recv(ofd->fd, buf, sizeof(buf), 0);
- if (len <= 0)
- return len;
+ read_len = read(ofd->fd, buf, sizeof(buf));
+ if (read_len <= 0) {
+ LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
+ return read_len;
+ }
- if (len != 158) {
+ if (read_len != 158) {
LOGP(DTRXD, LOGL_ERROR, "Got data message with invalid "
- "length '%d'\n", len);
+ "length '%zd'\n", read_len);
return -EINVAL;
}
tn = buf[0];
- fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
+ fn = osmo_load32be(buf + 1);
rssi = -(int8_t) buf[5];
toa256 = ((int16_t) (buf[6] << 8) | buf[7]);
@@ -537,8 +612,15 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
tn, fn, rssi, toa256);
+ /* Group the measurements together */
+ meas = (struct trx_meas_set) {
+ .toa256 = toa256,
+ .rssi = rssi,
+ .fn = fn,
+ };
+
/* Poke scheduler */
- sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa256);
+ sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, &meas);
/* Correct local clock counter */
if (fn % 51 == 0)
@@ -550,27 +632,27 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
uint8_t pwr, const ubit_t *bits)
{
- uint8_t buf[256];
+ uint8_t buf[TRXD_BUF_SIZE];
/**
* We must be sure that we have clock,
* and we have sent all control data
*
- * TODO: should we wait in TRX_STATE_RSP_WAIT state?
+ * TODO: introduce proper state machines for both
+ * transceiver and its TRXC interface.
*/
+#if 0
if (trx->fsm->state != TRX_STATE_ACTIVE) {
- LOGP(DTRXD, LOGL_DEBUG, "Ignoring TX data, "
+ LOGP(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);
buf[0] = tn;
- buf[1] = (fn >> 24) & 0xff;
- buf[2] = (fn >> 16) & 0xff;
- buf[3] = (fn >> 8) & 0xff;
- buf[4] = (fn >> 0) & 0xff;
+ osmo_store32be(fn, buf + 1);
buf[5] = pwr;
/* Copy ubits {0,1} */
@@ -582,52 +664,55 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
return 0;
}
-/*
- * Open/close OsmoTRX connection
- */
-
-int trx_if_open(struct trx_instance **trx, const char *local_host,
- const char *remote_host, uint16_t port)
+/* 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_new;
+ struct trx_instance *trx;
int rc;
- LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface\n");
+ LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface "
+ "(%s:%u)\n", remote_host, base_port);
/* Try to allocate memory */
- trx_new = talloc_zero(tall_trx_ctx, struct trx_instance);
- if (!trx_new) {
+ trx = talloc_zero(tall_ctx, struct trx_instance);
+ if (!trx) {
LOGP(DTRX, LOGL_ERROR, "Failed to allocate memory\n");
- return -ENOMEM;
+ 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);
+ return NULL;
}
/* Initialize CTRL queue */
- INIT_LLIST_HEAD(&trx_new->trx_ctrl_list);
+ INIT_LLIST_HEAD(&trx->trx_ctrl_list);
/* Open sockets */
- rc = trx_udp_open(trx_new, &trx_new->trx_ofd_ctrl, local_host,
- port + 101, remote_host, port + 1, trx_ctrl_read_cb);
+ rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, local_host,
+ base_port + 101, remote_host, base_port + 1, trx_ctrl_read_cb);
if (rc < 0)
- goto error;
+ goto udp_error;
- rc = trx_udp_open(trx_new, &trx_new->trx_ofd_data, local_host,
- port + 102, remote_host, port + 2, trx_data_rx_cb);
+ rc = trx_udp_open(trx, &trx->trx_ofd_data, local_host,
+ base_port + 102, remote_host, base_port + 2, trx_data_rx_cb);
if (rc < 0)
- goto error;
+ goto udp_error;
- /* Allocate a new dedicated state machine */
- osmo_fsm_register(&trx_fsm);
- trx_new->fsm = osmo_fsm_inst_alloc(&trx_fsm, trx_new,
- NULL, LOGL_DEBUG, "trx_interface");
-
- *trx = trx_new;
+ return trx;
- return 0;
-
-error:
+udp_error:
LOGP(DTRX, LOGL_ERROR, "Couldn't establish UDP connection\n");
- talloc_free(trx_new);
- return rc;
+ osmo_fsm_inst_free(trx->fsm);
+ talloc_free(trx);
+ return NULL;
}
/* Flush pending control messages */
@@ -666,3 +751,8 @@ void trx_if_close(struct trx_instance *trx)
osmo_fsm_inst_free(trx->fsm);
talloc_free(trx);
}
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&trx_fsm) == 0);
+}
diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h
index be0d41a6..2fafa56a 100644
--- a/src/host/trxcon/trx_if.h
+++ b/src/host/trxcon/trx_if.h
@@ -8,6 +8,9 @@
#include "scheduler.h"
#include "sched_trx.h"
+#define TRXC_BUF_SIZE 1024
+#define TRXD_BUF_SIZE 512
+
/* Forward declaration to avoid mutual include */
struct l1ctl_link;
@@ -25,7 +28,10 @@ struct trx_instance {
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;
@@ -46,14 +52,14 @@ struct trx_instance {
struct trx_ctrl_msg {
struct llist_head list;
- char cmd[128];
+ char cmd[TRXC_BUF_SIZE];
int retry_cnt;
int critical;
int cmd_len;
};
-int trx_if_open(struct trx_instance **trx, const char *local_host,
- const char *remote_host, uint16_t port);
+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);
@@ -67,6 +73,8 @@ 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);
diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c
index 251321d2..158e27f7 100644
--- a/src/host/trxcon/trxcon.c
+++ b/src/host/trxcon/trxcon.c
@@ -1,7 +1,7 @@
/*
* OsmocomBB <-> SDR connection bridge
*
- * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -38,6 +38,8 @@
#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/gsm/gsm_utils.h>
@@ -51,7 +53,7 @@
#include "sched_trx.h"
#define COPYRIGHT \
- "Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
+ "Copyright (C) 2016-2020 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" \
@@ -72,9 +74,11 @@ static struct {
const char *trx_remote_ip;
uint16_t trx_base_port;
uint32_t trx_fn_advance;
+ const char *gsmtap_ip;
} app_data;
-void *tall_trx_ctx = NULL;
+static void *tall_trxcon_ctx = NULL;
+struct gsmtap_inst *gsmtap = NULL;
struct osmo_fsm_inst *trxcon_fsm;
static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi,
@@ -93,7 +97,7 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi,
if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) {
/* Reset scheduler and clock counter */
- sched_trx_reset(app_data.trx, 1);
+ sched_trx_reset(app_data.trx, true);
/* TODO: implement trx_if_reset() */
trx_if_cmd_poweroff(app_data.trx);
@@ -156,8 +160,9 @@ static void print_help(void)
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(" -f --trx-advance Uplink burst scheduling advance (default 3)\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(" -D --daemonize Run as daemon\n");
}
@@ -176,11 +181,12 @@ static void handle_options(int argc, char **argv)
{"trx-remote", 1, 0, 'i'},
{"trx-port", 1, 0, 'p'},
{"trx-advance", 1, 0, 'f'},
+ {"gsmtap-ip", 1, 0, 'g'},
{"daemonize", 0, 0, 'D'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, "d:b:i:p:f:s:Dh",
+ c = getopt_long(argc, argv, "d:b:i:p:f:s:g:Dh",
long_options, &option_index);
if (c == -1)
break;
@@ -209,6 +215,9 @@ static void handle_options(int argc, char **argv)
case 's':
app_data.bind_socket = optarg;
break;
+ case 'g':
+ app_data.gsmtap_ip = optarg;
+ break;
case 'D':
app_data.daemonize = 1;
break;
@@ -224,25 +233,36 @@ static void init_defaults(void)
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.trx_fn_advance = 3;
app_data.debug_mask = NULL;
+ app_data.gsmtap_ip = NULL;
app_data.daemonize = 0;
app_data.quit = 0;
}
-static void signal_handler(int signal)
+static void signal_handler(int signum)
{
- fprintf(stderr, "signal %u received\n", signal);
+ fprintf(stderr, "signal %u received\n", signum);
- switch (signal) {
+ switch (signum) {
case SIGINT:
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_trx_ctx, stderr);
+ talloc_report_full(tall_trxcon_ctx, stderr);
break;
default:
break;
@@ -261,32 +281,52 @@ int main(int argc, char **argv)
talloc_enable_null_tracking();
/* Init talloc memory management system */
- tall_trx_ctx = talloc_init("trxcon context");
- msgb_talloc_ctx_init(tall_trx_ctx, 0);
+ tall_trxcon_ctx = talloc_init("trxcon context");
+ msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
/* Setup signal handlers */
signal(SIGINT, &signal_handler);
+ signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
/* Init logging system */
- trx_log_init(app_data.debug_mask);
+ trx_log_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);
+
+ /* Optional GSMTAP */
+ if (app_data.gsmtap_ip != NULL) {
+ gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1);
+ if (!gsmtap) {
+ LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP\n");
+ goto exit;
+ }
+ /* Suppress ICMP "destination unreachable" errors */
+ gsmtap_source_add_sink(gsmtap);
+ }
/* Allocate the application state machine */
- osmo_fsm_register(&trxcon_fsm_def);
- trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trx_ctx,
+ OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
+ trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trxcon_ctx,
NULL, LOGL_DEBUG, "main");
/* Init L1CTL server */
- rc = l1ctl_link_init(&app_data.l1l, app_data.bind_socket);
- if (rc)
+ app_data.l1l = l1ctl_link_init(tall_trxcon_ctx,
+ app_data.bind_socket);
+ if (app_data.l1l == NULL)
goto exit;
/* Init transceiver interface */
- rc = trx_if_open(&app_data.trx,
- app_data.trx_bind_ip, app_data.trx_remote_ip, app_data.trx_base_port);
- if (rc)
+ 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 */
@@ -330,8 +370,8 @@ exit:
* Print report for the root talloc context in order
* to be able to find and fix potential memory leaks.
*/
- talloc_report_full(tall_trx_ctx, stderr);
- talloc_free(tall_trx_ctx);
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ talloc_free(tall_trxcon_ctx);
/* Make both Valgrind and ASAN happy */
talloc_report_full(NULL, stderr);
diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h
index 65b5e85d..9a0792bf 100644
--- a/src/host/trxcon/trxcon.h
+++ b/src/host/trxcon/trxcon.h
@@ -3,7 +3,7 @@
#define GEN_MASK(state) (0x01 << state)
extern struct osmo_fsm_inst *trxcon_fsm;
-extern void *tall_trx_ctx;
+extern struct gsmtap_inst *gsmtap;
enum trxcon_fsm_states {
TRXCON_STATE_IDLE = 0,
diff --git a/src/host/virt_phy/configure.ac b/src/host/virt_phy/configure.ac
index c8012c99..fbff2c11 100644
--- a/src/host/virt_phy/configure.ac
+++ b/src/host/virt_phy/configure.ac
@@ -12,6 +12,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,6 +22,17 @@ 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_CONFIG_FILES([
Makefile
include/Makefile
diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
index 94174da4..d3562a17 100644
--- a/src/host/virt_phy/include/virtphy/l1ctl_sap.h
+++ b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
@@ -32,7 +32,6 @@ 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 */
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_model.h b/src/host/virt_phy/include/virtphy/virt_l1_model.h
index 67c24bb6..ffed7082 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_model.h
+++ b/src/host/virt_phy/include/virtphy/virt_l1_model.h
@@ -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,12 +68,13 @@ 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 {
@@ -99,6 +100,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;
};
diff --git a/src/host/virt_phy/include/virtphy/virtual_um.h b/src/host/virt_phy/include/virtphy/virtual_um.h
index 52f2df64..fe060929 100644
--- a/src/host/virt_phy/include/virtphy/virtual_um.h
+++ b/src/host/virt_phy/include/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/src/gsmtapl1_if.c b/src/host/virt_phy/src/gsmtapl1_if.c
index 2cf9d2dc..83b01fec 100644
--- a/src/host/virt_phy/src/gsmtapl1_if.c
+++ b/src/host/virt_phy/src/gsmtapl1_if.c
@@ -26,6 +26,7 @@
#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>
@@ -48,6 +49,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,7 +81,7 @@ 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 */
+ uint16_t arfcn;
uint8_t signal_dbm = 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) */
@@ -68,18 +92,41 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn
uint8_t timeslot; /* tdma timeslot to send in (0-7) */
uint8_t gsmtap_chan; /* the gsmtap channel */
+ switch (ms->state.state) {
+ case MS_STATE_DEDICATED:
+ case MS_STATE_TBF:
+ arfcn = ms->state.dedicated.band_arfcn;
+ break;
+ default:
+ arfcn = ms->state.serving_cell.arfcn;
+ break;
+ }
+
switch (l1h->msg_type) {
case L1CTL_DATA_TBF_REQ:
ul = NULL;
rsl_chantype = RSL_CHAN_OSMO_PDCH;
timeslot = tn;
subslot = 0;
- gsmtap_chan = chantype_rsl2gsmtap(rsl_chantype, 0);
+ gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, 0, false);
+ 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;
}
@@ -135,7 +182,7 @@ static bool gprs_dl_block_matches_ms(struct l1_model_ms *ms, struct msgb *msg, u
return true;
break;
case 1: /* RLC/MAC Control without optional octets */
- /* forward all RLC/MAC control blocks without optional octets, i.e. not adressed
+ /* forward all RLC/MAC control blocks without optional octets, i.e. not addressed
* to a specific TFI */
return true;
case 2: /* RLC/MAC with optional control octets */
@@ -198,18 +245,24 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
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,13 +272,7 @@ 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
@@ -235,10 +282,24 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg,
l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 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, signal_dbm, 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) */
diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c
index aac49bf0..bab62590 100644
--- a/src/host/virt_phy/src/l1ctl_sap.c
+++ b/src/host/virt_phy/src/l1ctl_sap.c
@@ -286,9 +286,11 @@ 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;
@@ -313,7 +315,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)
{
@@ -504,7 +506,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)
{
@@ -539,7 +541,7 @@ void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg)
uint8_t *data = msg->data + sizeof(struct l1ctl_hdr);
LOGPMS(DL1C, LOGL_ERROR, ms, "Rx SIM Request (length: %u, data: %s): UNSUPPORTED\n",
- len, osmo_hexdump(data, sizeof(data)));
+ len, osmo_hexdump(data, len));
}
@@ -563,7 +565,7 @@ static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg)
}
if (ms->state.state == MS_STATE_DEDICATED)
- LOGPMS(DL1C, LOGL_NOTICE, ms, "Harrd termiation of DEDICATED mode, fix L23!\n");
+ LOGPMS(DL1C, LOGL_NOTICE, ms, "Hard termination of DEDICATED mode, fix L23!\n");
if (cfg_req->is_uplink) {
for (i = 0; i < 8; i++)
diff --git a/src/host/virt_phy/src/l1ctl_sock.c b/src/host/virt_phy/src/l1ctl_sock.c
index bf28895f..7951f46e 100644
--- a/src/host/virt_phy/src/l1ctl_sock.c
+++ b/src/host/virt_phy/src/l1ctl_sock.c
@@ -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/logging.c b/src/host/virt_phy/src/logging.c
index a3e63bc5..11a21a37 100644
--- a/src/host/virt_phy/src/logging.c
+++ b/src/host/virt_phy/src/logging.c
@@ -70,28 +70,28 @@ static const struct log_info_cat default_categories[] = {
.description = "Layer 1 Control",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_DEBUG,
+ .loglevel = LOGL_INFO,
},
[DL1P] = {
.name = "DL1P",
.description = "Layer 1 Data",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_DEBUG,
+ .loglevel = LOGL_INFO,
},
[DVIRPHY] = {
.name = "DVIRPHY",
.description = "Virtual Layer 1 Interface",
.color = "\033[1;31m",
.enabled = 1,
- .loglevel = LOGL_DEBUG,
+ .loglevel = LOGL_INFO,
},
[DMAIN] = {
.name = "DMAIN",
.description = "Main Program / Data Structures",
.color = "\033[1;32m",
.enabled = 1,
- .loglevel = LOGL_DEBUG,
+ .loglevel = LOGL_INFO,
},
};
@@ -116,9 +116,10 @@ int ms_log_init(char *cat_mask)
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_print_filename2(stderr_target, LOG_FILENAME_PATH);
log_set_use_color(stderr_target, 0);
log_set_print_timestamp(stderr_target, 1);
+ log_set_print_category_hex(stderr_target, 0);
log_set_print_category(stderr_target, 1);
if (cat_mask)
log_parse_category_mask(stderr_target, cat_mask);
@@ -128,8 +129,8 @@ int ms_log_init(char *cat_mask)
const char *getL1ctlPrimName(uint8_t type)
{
- if (type <= ARRAY_SIZE(l1ctlPrimNames))
+ 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..d0c5b6df 100644
--- a/src/host/virt_phy/src/shared/osmo_mcast_sock.c
+++ b/src/host/virt_phy/src/shared/osmo_mcast_sock.c
@@ -43,9 +43,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..ef3ad379 100644
--- a/src/host/virt_phy/src/shared/virtual_um.c
+++ b/src/host/virt_phy/src/shared/virtual_um.c
@@ -27,7 +27,9 @@
#include <osmocom/core/talloc.h>
#include <virtphy/osmo_mcast_sock.h>
#include <virtphy/virtual_um.h>
+
#include <unistd.h>
+#include <errno.h>
/**
* Virtual UM interface file descriptor callback.
@@ -37,49 +39,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 +121,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_sched_simple.c b/src/host/virt_phy/src/virt_l1_sched_simple.c
index 4737135c..486d319a 100644
--- a/src/host/virt_phy/src/virt_l1_sched_simple.c
+++ b/src/host/virt_phy/src/virt_l1_sched_simple.c
@@ -97,6 +97,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 +130,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..656ff800 100644
--- a/src/host/virt_phy/src/virt_prim_data.c
+++ b/src/host/virt_phy/src/virt_prim_data.c
@@ -47,7 +47,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,7 +74,7 @@ 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);
@@ -105,7 +106,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 +124,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_pm.c b/src/host/virt_phy/src/virt_prim_pm.c
index 46370138..08d9b054 100644
--- a/src/host/virt_phy/src/virt_prim_pm.c
+++ b/src/host/virt_phy/src/virt_prim_pm.c
@@ -84,38 +84,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_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);
+}
- 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);
+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 +148,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 +158,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_traffic.c b/src/host/virt_phy/src/virt_prim_traffic.c
index 5f6b273b..0e08a126 100644
--- a/src/host/virt_phy/src/virt_prim_traffic.c
+++ b/src/host/virt_phy/src/virt_prim_traffic.c
@@ -47,7 +47,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);
}
/**
@@ -84,7 +85,8 @@ void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arf
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));
@@ -101,11 +103,16 @@ void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arf
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 118e8a10..412da9c8 100644
--- a/src/host/virt_phy/src/virtphy.c
+++ b/src/host/virt_phy/src/virtphy.c
@@ -59,6 +59,8 @@ 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()
{
@@ -76,6 +78,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 +95,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 +130,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;
}
@@ -153,6 +165,8 @@ void parse_arfcn_sig_lev_red(struct l1_model_ms *model, char * arfcn_sig_lev_red
return;
char *token = strtok(arfcn_sig_lev_red_mask, ":");
+ if (!token)
+ return;
do {
char* colon = strstr(token, ",");
uint16_t arfcn;
@@ -229,8 +243,8 @@ int main(int argc, char *argv[])
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/target/firmware/Makefile b/src/target/firmware/Makefile
index 22fa7461..104ddac9 100644
--- a/src/target/firmware/Makefile
+++ b/src/target/firmware/Makefile
@@ -24,7 +24,7 @@ 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 pirelli_dpl10
+BOARDS?=compal_e88 compal_e86 compal_e99 se_j100 gta0x gtm900b fcdev3b pirelli_dpl10
# Framebuffer support, board specific drivers
FB_OBJS=fb/framebuffer.o fb/font.o fb/helvR08.o fb/helvB14.o fb/c64.o \
@@ -38,43 +38,65 @@ FB_dpl10_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_s6b33b1x.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/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_tables.o \
+ board/gta0x/afcparams.o \
+ board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS)
+BOARD_fcdev3b_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)
@@ -84,11 +106,12 @@ BOARD_se_j100_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS)
#
# 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
@@ -101,6 +124,7 @@ ANY_APP_LIBS+= calypso/libcalypso.a \
layer1/liblayer1.a \
lib/libmini.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
@@ -117,9 +141,10 @@ INCLUDES=-Iinclude/ -I../../../include -I../../shared/libosmocore/include -I../.
-include calypso/Makefile
-include layer1/Makefile
-include comm/Makefile
+-include tiffs/Makefile
-include lib/Makefile
-# Include rules
+# Include toolchain configuration
-include Makefile.inc
diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc
index 90498989..2be240d2 100644
--- a/src/target/firmware/Makefile.inc
+++ b/src/target/firmware/Makefile.inc
@@ -1,7 +1,7 @@
#### TOOLCHAIN CONFIGURATION ####
-CROSS_COMPILE?=arm-elf-
+CROSS_COMPILE?=arm-none-eabi-
CC=gcc
LD=ld
@@ -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=
diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c
index e4fcf4f0..5b792d68 100644
--- a/src/target/firmware/abb/twl3025.c
+++ b/src/target/firmware/abb/twl3025.c
@@ -60,8 +60,15 @@ const uint16_t twl3025_default_ramp[16] = {
ABB_RAMP_VAL( 0, 0),
};
+typedef enum pwon_state {
+ PWON_IDLE,
+ PWON_DETECTED,
+ PWON_REPORTED,
+} pwon_state_t;
+
struct twl3025 {
uint8_t page;
+ pwon_state_t pwon;
};
static struct twl3025 twl3025_state;
@@ -106,6 +113,8 @@ static void twl3025_irq(enum irq_nr nr)
case IRQ_EXTERNAL: // charger in/out, pwrbtn, adc done
src = twl3025_reg_read(ITSTATREG);
// printd("itstatreg 0x%02x\n", src);
+ if ((src & 0x04) && twl3025_state.pwon == PWON_IDLE)
+ twl3025_state.pwon = PWON_DETECTED;
if (src & 0x08)
handle_charger();
if (src & 0x20)
@@ -127,6 +136,7 @@ void twl3025_init(void)
twl3025_clk13m(1);
twl3025_reg_write(AFCCTLADD, 0x01); /* AFCCK(1:0) must not be zero! */
twl3025_unit_enable(TWL3025_UNIT_AFC, 1);
+ twl3025_state.pwon = PWON_IDLE;
irq_register_handler(IRQ_EXTERNAL, &twl3025_irq);
irq_config(IRQ_EXTERNAL, 0, 0, 0);
@@ -185,6 +195,24 @@ static void twl3025_wait_ibic_access(void)
delay_ms(1);
}
+int twl3025_get_pwon(void)
+{
+ switch (twl3025_state.pwon) {
+ case PWON_DETECTED:
+ twl3025_state.pwon = PWON_REPORTED;
+ break;
+ case PWON_REPORTED:
+ if (twl3025_reg_read(VRPCSTS) & 0x10)
+ twl3025_state.pwon = PWON_IDLE;
+ break;
+ case PWON_IDLE:
+ default:
+ break;
+ }
+
+ return (twl3025_state.pwon != PWON_IDLE);
+}
+
void twl3025_power_off(void)
{
unsigned long flags;
@@ -201,6 +229,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) {
diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c
index 59ffe972..b2e66e2c 100644
--- a/src/target/firmware/apps/layer1/main.c
+++ b/src/target/firmware/apps/layer1/main.c
@@ -33,6 +33,7 @@
#include <abb/twl3025.h>
#include <rf/trf6151.h>
+#include <rf/readcal.h>
#include <comm/sercomm.h>
#include <comm/timer.h>
@@ -106,6 +107,7 @@ int main(void)
memset(atr,0,sizeof(atr));
atrLength = calypso_sim_powerup(atr);
+ read_factory_rf_calibration();
layer1_init();
tpu_frame_irq_en(1, 1);
diff --git a/src/target/firmware/apps/menu/main.c b/src/target/firmware/apps/menu/main.c
new file mode 100644
index 00000000..fa369681
--- /dev/null
+++ b/src/target/firmware/apps/menu/main.c
@@ -0,0 +1,337 @@
+/* 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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..b04fe28d 100644
--- a/src/target/firmware/apps/rssi/main.c
+++ b/src/target/firmware/apps/rssi/main.c
@@ -34,6 +34,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 +171,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();
@@ -1260,7 +1261,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 +1529,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/snake_game/main.c b/src/target/firmware/apps/snake_game/main.c
new file mode 100644
index 00000000..44dda9af
--- /dev/null
+++ b/src/target/firmware/apps/snake_game/main.c
@@ -0,0 +1,521 @@
+/* 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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/board/common/readcal_tiffs.c b/src/target/firmware/board/common/readcal_tiffs.c
new file mode 100644
index 00000000..43519710
--- /dev/null
+++ b/src/target/firmware/board/common/readcal_tiffs.c
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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..9901e712
--- /dev/null
+++ b/src/target/firmware/board/common/tx_calchan.c
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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/keymap.h b/src/target/firmware/board/compal/keymap.h
index ce8f9c24..fe0e3039 100644
--- a/src/target/firmware/board/compal/keymap.h
+++ b/src/target/firmware/board/compal/keymap.h
@@ -23,5 +23,6 @@ static const uint8_t keymap[] = {
[KEY_OK] = 0,
[KEY_POWER] = 24,
[KEY_MINUS] = 30, /* not existent */
- [KEY_PLUS] = 31, /* not existent */
+ [KEY_PLUS] = 30, /* not existent */
+ [KEY_CAMERA] = 30, /* not existent */
};
diff --git a/src/target/firmware/board/compal/ram.lds b/src/target/firmware/board/compal/ram.lds
index b52b619a..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
@@ -20,10 +20,10 @@ SECTIONS
. = 0x800000;
/* romloader data section, contains passthru interrupt vectors */
- .compal.loader (NOLOAD) : { . = 0x100; } > LRAM
+ .compal.loader (NOLOAD) : { . = 0x1c; } > LRAM
/* image signature (prepended by osmocon according to phone type) */
- .compal.header (NOLOAD) : { . = 4; } > LRAM
+ .compal.header 0x800100 (NOLOAD): { . = 4; } > LRAM
/* initialization code */
. = ALIGN(4);
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..2f7944ce
--- /dev/null
+++ b/src/target/firmware/board/compal/readcal_common.c
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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..a036e111
--- /dev/null
+++ b/src/target/firmware/board/compal/readcal_small.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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..dd7411da
--- /dev/null
+++ b/src/target/firmware/board/compal/rf_tables.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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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/rffe_dualband.c b/src/target/firmware/board/compal/rffe_dualband.c
index de161899..faa68ff5 100644
--- a/src/target/firmware/board/compal/rffe_dualband.c
+++ b/src/target/firmware/board/compal/rffe_dualband.c
@@ -44,10 +44,30 @@ void rffe_mode(enum gsm_band band, int tx)
tsp_act_update(tspact);
}
-/* Returns RF wiring */
+/*
+ * Each given Mot C1xx phone is made either for 900+1800 MHz, in which
+ * case only the DCS Rx port is connected, or for 850+1900 MHz, in which
+ * case only the PCS Rx port is connected. Here we tell the TRF6151 driver
+ * that both DCS and PCS ports are connected, so that the same binary
+ * build can be used on both EU-band and US-band C1xx phones.
+ *
+ * If you are using your phone the way it was meant to be used, i.e.,
+ * listening to EGSM and DCS downlinks only with EU-band phones or
+ * listening to GSM850 and PCS downlinks only with US-band phones, then
+ * the same standard binary build will work on both: the TRF6151 driver
+ * will use the DCS Rx port when trying to receive DCS downlink or the
+ * PCS Rx port when trying to receive PCS downlink, and everything will
+ * just work. However, if you are interested in using OsmocomBB for
+ * various hacking purposes (its original and primary intended use)
+ * and you need to tune your phone's TRF6151 receiver out of spec or
+ * at least outside of the DCS/PCS Rx SAW filter's legitimate passband
+ * (or if you have changed or removed that SAW filter), then you need
+ * to change the following rffe_get_rx_ports() function to match your
+ * specific hw version, i.e., PORT_DCS1800 only or PORT_PCS1900 only.
+ */
uint32_t rffe_get_rx_ports(void)
{
- return (1 << PORT_LO) | (1 << PORT_DCS1800);
+ return (1 << PORT_LO) | (1 << PORT_DCS1800) | (1 << PORT_PCS1900);
}
uint32_t rffe_get_tx_ports(void)
diff --git a/src/target/firmware/board/compal_e86/init.c b/src/target/firmware/board/compal_e86/init.c
index fed9f85a..725f1841 100644
--- a/src/target/firmware/board/compal_e86/init.c
+++ b/src/target/firmware/board/compal_e86/init.c
@@ -31,6 +31,7 @@
#include <keypad.h>
#include <console.h>
#include <flash/cfi_flash.h>
+#include <tiffs.h>
#include <calypso/irq.h>
#include <calypso/clock.h>
@@ -146,4 +147,7 @@ void board_init(int with_irq)
/* enable LEDB driver of Iota for keypad backlight */
twl3025_reg_write(AUXLED, 0x02);
+
+ /* Initialize TIFFS reader (5 sectors of 64 KiB each) */
+ tiffs_init(0x370000, 0x10000, 5);
}
diff --git a/src/target/firmware/board/compal_e86/rffe_dualband_e86.c b/src/target/firmware/board/compal_e86/rffe_dualband_e86.c
index 4ad85dcd..c6dccd3d 100644
--- a/src/target/firmware/board/compal_e86/rffe_dualband_e86.c
+++ b/src/target/firmware/board/compal_e86/rffe_dualband_e86.c
@@ -48,10 +48,30 @@ void rffe_mode(enum gsm_band band, int tx)
tsp_act_update(tspact);
}
-/* Returns RF wiring */
+/*
+ * Each given Mot C1xx phone is made either for 900+1800 MHz, in which
+ * case only the DCS Rx port is connected, or for 850+1900 MHz, in which
+ * case only the PCS Rx port is connected. Here we tell the TRF6151 driver
+ * that both DCS and PCS ports are connected, so that the same binary
+ * build can be used on both EU-band and US-band C1xx phones.
+ *
+ * If you are using your phone the way it was meant to be used, i.e.,
+ * listening to EGSM and DCS downlinks only with EU-band phones or
+ * listening to GSM850 and PCS downlinks only with US-band phones, then
+ * the same standard binary build will work on both: the TRF6151 driver
+ * will use the DCS Rx port when trying to receive DCS downlink or the
+ * PCS Rx port when trying to receive PCS downlink, and everything will
+ * just work. However, if you are interested in using OsmocomBB for
+ * various hacking purposes (its original and primary intended use)
+ * and you need to tune your phone's TRF6151 receiver out of spec or
+ * at least outside of the DCS/PCS Rx SAW filter's legitimate passband
+ * (or if you have changed or removed that SAW filter), then you need
+ * to change the following rffe_get_rx_ports() function to match your
+ * specific hw version, i.e., PORT_DCS1800 only or PORT_PCS1900 only.
+ */
uint32_t rffe_get_rx_ports(void)
{
- return (1 << PORT_LO) | (1 << PORT_DCS1800);
+ return (1 << PORT_LO) | (1 << PORT_DCS1800) | (1 << PORT_PCS1900);
}
uint32_t rffe_get_tx_ports(void)
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..7bac4d51
--- /dev/null
+++ b/src/target/firmware/board/compal_e86/tx_ramps.c
@@ -0,0 +1,435 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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/flash.lds b/src/target/firmware/board/compal_e88/flash.lds
index 67d727ff..52148d92 100644
--- a/src/target/firmware/board/compal_e88/flash.lds
+++ b/src/target/firmware/board/compal_e88/flash.lds
@@ -91,8 +91,8 @@ SECTIONS
PROVIDE(_got_end = ADDR(.got) + SIZEOF(.got));
/* reserved ram */
- .compal.reservedram 0x800000 (NOLOAD) : {
- . = 0xff;
+ .compal.reservedram 0x800038 (NOLOAD) : {
+ . = 0xff - 0x38;
} > IRAM
/* initialized data */
diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c
index 04ae4588..956f7dc5 100755
--- a/src/target/firmware/board/compal_e88/init.c
+++ b/src/target/firmware/board/compal_e88/init.c
@@ -30,6 +30,7 @@
#include <keypad.h>
#include <console.h>
#include <flash/cfi_flash.h>
+#include <tiffs.h>
#include <calypso/irq.h>
#include <calypso/clock.h>
@@ -142,4 +143,7 @@ void board_init(int with_irq)
/* Initialize the charging controller */
battery_compal_e88_init();
+
+ /* Initialize TIFFS reader (6 sectors of 8 KiB each) */
+ tiffs_init(0x001f0000, 0x2000, 6);
}
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..846a21d4
--- /dev/null
+++ b/src/target/firmware/board/compal_e88/tx_ramps.c
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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 7cc3c67d..0271f16e 100644
--- a/src/target/firmware/board/compal_e99/init.c
+++ b/src/target/firmware/board/compal_e99/init.c
@@ -51,6 +51,7 @@
#define ARMIO_LATCH_OUT 0xfffe4802
#define IO_CNTL_REG 0xfffe4804
+#define ARM_CONF_REG 0xfffef006
#define ASIC_CONF_REG 0xfffef008
static void board_io_init(void)
@@ -76,6 +77,11 @@ static void board_io_init(void)
reg &= ~(1 << 3);
reg |= (1 << 1);
writew(reg, 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);
}
void board_init(int with_irq)
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..c75e1fa5
--- /dev/null
+++ b/src/target/firmware/board/compal_e99/readcal.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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
new file mode 100644
index 00000000..c0705364
--- /dev/null
+++ b/src/target/firmware/board/fcdev3b/init.c
@@ -0,0 +1,145 @@
+/* Initialization for the FreeCalypso FCDEV3B modem */
+
+/* Based on board/gta0x/init.c with the following obnoxious legalese:
+ *
+ * (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 <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>
+
+#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);
+ /* TWL3025: Set SPI+RIF RX clock to rising edge */
+ reg |= (1 << 13) | (1 << 14);
+ 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.
+ *
+ * Differences from Openmoko GTA0x:
+ *
+ * GPIO1 output needs to be 1 to enable the loudspeaker amplifier
+ * GPIO3 needs to be configured as an input
+ * I/O 9-12 are MCSI rather than GPIOs
+ */
+ writew(0x0215, IO_CONF_REG);
+ writew(0xDC08, IO_CNTL_REG);
+ writew(0x0002, 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);
+}
+
+void board_init(int with_irq)
+{
+ /* Configure the memory interface */
+ /* Using the same settings as the official FreeCalypso fw */
+ calypso_mem_cfg(CALYPSO_nCS0, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS2, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS3, 4, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_CS4, 4, CALYPSO_MEM_16bit, 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();
+
+ 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 ABB driver (uses SPI) */
+ twl3025_init();
+
+ /* Initialize TIFFS reader (8 sectors of 256 KiB each) */
+ tiffs_init(0x01800000, 0x40000, 8);
+}
diff --git a/src/target/firmware/board/gta0x/afcparams.c b/src/target/firmware/board/gta0x/afcparams.c
new file mode 100644
index 00000000..ba48360d
--- /dev/null
+++ b/src/target/firmware/board/gta0x/afcparams.c
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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 4f49e802..7fba7561 100644
--- a/src/target/firmware/board/gta0x/init.c
+++ b/src/target/firmware/board/gta0x/init.c
@@ -30,6 +30,7 @@
#include <keypad.h>
#include <console.h>
#include <flash/cfi_flash.h>
+#include <tiffs.h>
#include <calypso/irq.h>
#include <calypso/clock.h>
@@ -49,29 +50,26 @@
#define ARMIO_LATCH_OUT 0xfffe4802
#define IO_CNTL_REG 0xfffe4804
#define ASIC_CONF_REG 0xfffef008
+#define IO_CONF_REG 0xfffef00a
static void board_io_init(void)
{
uint16_t reg;
reg = readw(ASIC_CONF_REG);
- /* LCD Set I/O(3) / SA0 to I/O(3) mode */
- reg &= ~(1 << 10);
- /* Set function pins to I2C Mode */
- reg |= ((1 << 12) | (1 << 7)); /* SCL / SDA */
/* TWL3025: Set SPI+RIF RX clock to rising edge */
reg |= (1 << 13) | (1 << 14);
writew(reg, ASIC_CONF_REG);
- /* LCD Set I/O(3) to output mode */
- reg = readw(IO_CNTL_REG);
- reg &= ~(1 << 3);
- writew(reg, IO_CNTL_REG);
-
- /* LCD Set I/O(3) output low */
- reg = readw(ARMIO_LATCH_OUT);
- reg &= ~(1 << 3);
- writew(reg, ARMIO_LATCH_OUT);
+ /*
+ * 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.
+ */
+ writew(0x03F5, IO_CONF_REG);
+ writew(0xC000, IO_CNTL_REG);
+ writew(0x0000, ARMIO_LATCH_OUT);
}
void board_init(int with_irq)
@@ -135,4 +133,7 @@ void board_init(int with_irq)
/* Initialize ABB driver (uses SPI) */
twl3025_init();
+
+ /* Initialize TIFFS reader (7 sectors of 64 KiB each) */
+ tiffs_init(0x380000, 0x10000, 7);
}
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..9a08b3c7
--- /dev/null
+++ b/src/target/firmware/board/gta0x/rf_tables.c
@@ -0,0 +1,573 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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..ca8908dc
--- /dev/null
+++ b/src/target/firmware/board/gtm900b/afcparams.c
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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
new file mode 100644
index 00000000..934e96e8
--- /dev/null
+++ b/src/target/firmware/board/gtm900b/init.c
@@ -0,0 +1,221 @@
+/* Initialization for the Huawei GTM900-B modem */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-19 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#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 "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
+#define LPG_LCR_REG 0xfffe7800
+#define LPG_PM_REG 0xfffe7801
+
+int gtm900_hw_is_mg01gsmt;
+
+static void board_io_init(void)
+{
+ uint16_t reg;
+
+ reg = readw(ASIC_CONF_REG);
+ /* Set LPG pin mux for power LED */
+ reg |= (1 << 6);
+ /* TWL3025: Set SPI+RIF RX clock to rising edge */
+ reg |= (1 << 13) | (1 << 14);
+ writew(reg, ASIC_CONF_REG);
+
+ /*
+ * Configure Calypso GPIO and multifunction pins the same way
+ * how Huawei's official firmware configures them.
+ */
+ writew(0x03F5, IO_CONF_REG);
+ 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.
+ * 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);
+ 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_IRDA);
+ cons_bind_uart(UART_MODEM);
+
+ /* initialize IRDA UART to be used for sercomm */
+ uart_init(UART_IRDA, with_irq);
+ uart_baudrate(UART_IRDA, UART_115200);
+
+ /* Initialize MODEM UART to be used for old-school console code. */
+ uart_init(UART_MODEM, with_irq);
+ uart_baudrate(UART_MODEM, 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 board flash */
+ board_flash_init();
+}
diff --git a/src/target/firmware/board/gtm900b/keymap.h b/src/target/firmware/board/gtm900b/keymap.h
new file mode 100644
index 00000000..07a19e01
--- /dev/null
+++ b/src/target/firmware/board/gtm900b/keymap.h
@@ -0,0 +1,29 @@
+/* keymap for the Huawei GTM900-B */
+
+static const uint8_t keymap[] = {
+ [KEY_0] = 0,
+ [KEY_1] = 0,
+ [KEY_2] = 0,
+ [KEY_3] = 0,
+ [KEY_4] = 0,
+ [KEY_5] = 0,
+ [KEY_6] = 0,
+ [KEY_7] = 0,
+ [KEY_8] = 0,
+ [KEY_9] = 0,
+ [KEY_STAR] = 0,
+ [KEY_HASH] = 0,
+ [KEY_MENU] = 0,
+ [KEY_LEFT_SB] = 0,
+ [KEY_RIGHT_SB] = 0,
+ [KEY_UP] = 0,
+ [KEY_DOWN] = 0,
+ [KEY_LEFT] = 0,
+ [KEY_RIGHT] = 0,
+ [KEY_OK] = 0,
+/* power button is not connected to keypad scan matrix but to TWL3025 */
+ [KEY_POWER] = 31,
+ [KEY_MINUS] = 0,
+ [KEY_PLUS] = 0,
+ [KEY_CAMERA] = 0,
+};
diff --git a/src/target/firmware/board/gtm900b/rffe_gtm900b.c b/src/target/firmware/board/gtm900b/rffe_gtm900b.c
new file mode 100644
index 00000000..3efb5e2a
--- /dev/null
+++ b/src/target/firmware/board/gtm900b/rffe_gtm900b.c
@@ -0,0 +1,197 @@
+/* RF frontend driver for Huawei GTM900-B modems, supporting both
+ * MG01GSMT and MGCxGSMT hardware variants */
+
+/* (C) 2019 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>
+#include <flash/cfi_flash.h>
+
+/* This is a value that has been measured for the GTM900-B: 74dBm,
+ 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 74
+
+/* describe how the RF frontend is wired on the Huawei GTM900-B */
+#define IOTA_STROBE TSPEN(0) /* Strobe for the Iota TSP */
+#define RITA_STROBE TSPEN(2) /* Strobe for the Rita TSP */
+
+#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */
+#define PA_BAND_SELECT TSPACT(3) /* Low: 850/900, High: 1800/1900 */
+#define PA_TX_ENABLE TSPACT(9) /* Enable the Power Amplifier */
+
+/* MGC2GSMT Ver. BRF specific antenna switch signals, low active */
+#define ASM_VC1 TSPACT(1) /* low on GSM900 TX */
+#define ASM_VC2 TSPACT(2) /* low on DCS1800 TX */
+
+/* MG01GSMT Ver. C specific antenna switch signals, low active */
+#define CXG_CTLA TSPACT(4) /* CXG1192UR CTLA input */
+#define CXG_CTLB TSPACT(1) /* CXG1192UR CTLB input */
+#define CXG_CTLC TSPACT(2) /* CXG1192UR CTLC input */
+
+/*
+ * The Sony CXG1192UR switch is wired as follows on the MG01GSMT Ver. C:
+ *
+ * Rx1: GSM850 RX filter (Epcos B5013)
+ * Rx2: GSM900 RX filter (Epcos B7710)
+ * Rx3: DCS1800 RX filter (Epcos 7714)
+ * Rx4: PCS1900 RX filter (not populated)
+ * Tx1: low band PA output
+ * Tx2: high band PA output
+ */
+
+extern int gtm900_hw_is_mg01gsmt; /* set in init.c */
+
+static inline void rffe_mode_mgc2gsmt(enum gsm_band band, int tx)
+{
+ uint16_t tspact = tsp_act_state();
+
+ /* First we mask off all bits from the state cache */
+ tspact &= ~(PA_BAND_SELECT | PA_TX_ENABLE);
+ tspact |= (ASM_VC1 | ASM_VC2); /* low-active */
+
+#ifdef CONFIG_TX_ENABLE
+ /* Then we selectively set the bits on, if required */
+ if (tx) {
+ tspact |= PA_TX_ENABLE;
+ tspact &= ~CXG_CTLA;
+
+ if (band == GSM_BAND_1800 || band == GSM_BAND_1900) {
+ tspact |= PA_BAND_SELECT;
+ tspact &= ~ASM_VC2;
+ } else {
+ tspact &= ~ASM_VC1;
+ }
+ }
+#endif /* TRANSMIT_SUPPORT */
+
+ tsp_act_update(tspact);
+}
+
+static inline void rffe_mode_mg01gsmt(enum gsm_band band, int tx)
+{
+ uint16_t tspact = tsp_act_state();
+
+ /* First we mask off all bits from the state cache */
+ tspact &= ~(PA_BAND_SELECT | PA_TX_ENABLE);
+ tspact |= (CXG_CTLA | CXG_CTLB | CXG_CTLC); /* low-active */
+
+ switch (band) {
+ case GSM_BAND_850:
+ tspact &= ~CXG_CTLB; /* select Ant1 - Rx1 */
+ break;
+ case GSM_BAND_900:
+ tspact &= ~CXG_CTLC; /* select Ant1 - Rx2 */
+ break;
+ case GSM_BAND_1800: /* select Ant2 - Rx3 */
+ break;
+ case GSM_BAND_1900:
+ tspact &= ~(CXG_CTLB | CXG_CTLC); /* select Ant2 - Rx4 */
+ break;
+ default:
+ break;
+ }
+
+#ifdef CONFIG_TX_ENABLE
+ /* Then we selectively set the bits on, if required */
+ if (tx) {
+ tspact |= PA_TX_ENABLE;
+ tspact &= ~CXG_CTLA;
+
+ if (band == GSM_BAND_1800 || band == GSM_BAND_1900) {
+ tspact |= PA_BAND_SELECT;
+ tspact &= ~CXG_CTLB;
+ }
+ }
+#endif /* TRANSMIT_SUPPORT */
+
+ tsp_act_update(tspact);
+}
+
+/* switch RF Frontend Mode */
+void rffe_mode(enum gsm_band band, int tx)
+{
+ if (gtm900_hw_is_mg01gsmt)
+ rffe_mode_mg01gsmt(band, tx);
+ else
+ rffe_mode_mgc2gsmt(band, tx);
+}
+
+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 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);
+
+ /* 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/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/pirelli_dpl10/init.c b/src/target/firmware/board/pirelli_dpl10/init.c
index 4c74a6d3..1af6e7c9 100644
--- a/src/target/firmware/board/pirelli_dpl10/init.c
+++ b/src/target/firmware/board/pirelli_dpl10/init.c
@@ -31,6 +31,7 @@
#include <keypad.h>
#include <console.h>
#include <flash/cfi_flash.h>
+#include <tiffs.h>
#include <calypso/irq.h>
#include <calypso/clock.h>
@@ -60,8 +61,8 @@ static void board_io_init(void)
uint16_t reg;
reg = readw(ASIC_CONF_REG);
- /* Set function pins to I2C Mode */
- reg |= ((1 << 12) | (1 << 7)); /* SCL / SDA */
+ /* Set LPG and PWL pin mux like Pirelli's fw does */
+ reg |= (1 << 6) | (1 << 4);
/* TWL3025: Set SPI+RIF RX clock to rising edge */
reg |= (1 << 13) | (1 << 14);
reg &= ~(1 << 1);
@@ -156,4 +157,7 @@ void board_init(int with_irq)
/* enable LEDB driver of Iota for keypad backlight */
twl3025_reg_write(AUXLED, 0x02);
+
+ /* Initialize TIFFS reader (18 sectors of 256 KiB each) */
+ tiffs_init(0x02000000, 0x40000, 18);
}
diff --git a/src/target/firmware/board/pirelli_dpl10/keymap.h b/src/target/firmware/board/pirelli_dpl10/keymap.h
index b85621bd..b06f17d3 100644
--- a/src/target/firmware/board/pirelli_dpl10/keymap.h
+++ b/src/target/firmware/board/pirelli_dpl10/keymap.h
@@ -21,8 +21,9 @@ static const uint8_t keymap[] = {
[KEY_LEFT] = 5,
[KEY_RIGHT] = 10,
[KEY_OK] = 11,
-/* power button is not connected, we use the camera button instead */
- [KEY_POWER] = 23,
+/* power button is not connected to keypad scan matrix but to TWL3025 */
+ [KEY_POWER] = 31,
[KEY_MINUS] = 22,
[KEY_PLUS] = 21,
+ [KEY_CAMERA] = 23,
};
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..6a4670a0
--- /dev/null
+++ b/src/target/firmware/board/pirelli_dpl10/readcal.c
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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..34fbbcbc
--- /dev/null
+++ b/src/target/firmware/board/pirelli_dpl10/rf_tables.c
@@ -0,0 +1,597 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License 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 <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/tx_ramps.c b/src/target/firmware/board/se_j100/tx_ramps.c
new file mode 100644
index 00000000..b1110e06
--- /dev/null
+++ b/src/target/firmware/board/se_j100/tx_ramps.c
@@ -0,0 +1,441 @@
+/*
+ * This code was written by Mychaela Falconia <falcon@freecalypso.org>