summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile90
-rw-r--r--src/README.building20
-rw-r--r--src/README.development57
-rwxr-xr-xsrc/host/calypso_pll/pll.pl10
-rw-r--r--src/host/gsm48-andreas/issues.txt99
-rw-r--r--src/host/layer23/.gitignore14
-rw-r--r--src/host/layer23/COPYING339
-rw-r--r--src/host/layer23/Makefile.am3
-rw-r--r--src/host/layer23/README42
-rw-r--r--src/host/layer23/configure.ac28
-rw-r--r--src/host/layer23/include/Makefile.am2
l---------src/host/layer23/include/l1a_l23_interface.h1
-rw-r--r--src/host/layer23/include/osmocom/Makefile.am2
-rw-r--r--src/host/layer23/include/osmocom/file.h80
-rwxr-xr-xsrc/host/layer23/include/osmocom/gsm322.h199
-rw-r--r--src/host/layer23/include/osmocom/gsm48_cc.h18
-rw-r--r--src/host/layer23/include/osmocom/gsm48_mm.h227
-rw-r--r--src/host/layer23/include/osmocom/gsm48_rr.h167
-rw-r--r--src/host/layer23/include/osmocom/gsmtap_util.h16
-rw-r--r--src/host/layer23/include/osmocom/l1ctl.h45
-rw-r--r--src/host/layer23/include/osmocom/l1l2_interface.h8
-rw-r--r--src/host/layer23/include/osmocom/l23_app.h10
-rw-r--r--src/host/layer23/include/osmocom/lapdm.h94
-rw-r--r--src/host/layer23/include/osmocom/layer3.h14
-rw-r--r--src/host/layer23/include/osmocom/logging.h25
-rw-r--r--src/host/layer23/include/osmocom/mncc.h166
-rw-r--r--src/host/layer23/include/osmocom/networks.h22
-rw-r--r--src/host/layer23/include/osmocom/osmocom_data.h80
-rw-r--r--src/host/layer23/include/osmocom/rslms.h23
-rw-r--r--src/host/layer23/include/osmocom/settings.h34
-rw-r--r--src/host/layer23/include/osmocom/subscriber.h89
-rw-r--r--src/host/layer23/include/osmocom/support.h93
-rw-r--r--src/host/layer23/include/osmocom/sysinfo.h120
-rw-r--r--src/host/layer23/include/osmocom/transaction.h71
-rw-r--r--src/host/layer23/include/osmocom/vty.h19
-rw-r--r--src/host/layer23/src/Makefile.am26
-rw-r--r--src/host/layer23/src/app_bcch_scan.c57
-rw-r--r--src/host/layer23/src/app_echo_test.c56
-rw-r--r--src/host/layer23/src/app_mobile.c191
-rw-r--r--src/host/layer23/src/app_phone.c60
-rw-r--r--src/host/layer23/src/bcch_scan.c319
-rw-r--r--src/host/layer23/src/gsm322.c3497
-rw-r--r--src/host/layer23/src/gsm48_cc.c2124
-rw-r--r--src/host/layer23/src/gsm48_mm.c4100
-rw-r--r--src/host/layer23/src/gsm48_rr.c4232
-rw-r--r--src/host/layer23/src/gsmtap_util.c179
-rw-r--r--src/host/layer23/src/l1ctl.c577
-rw-r--r--src/host/layer23/src/l1l2_interface.c167
-rw-r--r--src/host/layer23/src/lapdm.c2126
-rw-r--r--src/host/layer23/src/layer3.c235
-rw-r--r--src/host/layer23/src/logging.c112
-rw-r--r--src/host/layer23/src/main.c212
-rw-r--r--src/host/layer23/src/mnccms.c458
-rw-r--r--src/host/layer23/src/networks.c1824
-rw-r--r--src/host/layer23/src/rslms.c156
-rw-r--r--src/host/layer23/src/settings.c87
-rw-r--r--src/host/layer23/src/subscriber.c319
-rw-r--r--src/host/layer23/src/support.c155
-rw-r--r--src/host/layer23/src/sysinfo.c178
-rw-r--r--src/host/layer23/src/telnet_interface.c240
-rw-r--r--src/host/layer23/src/transaction.c143
-rw-r--r--src/host/layer23/src/vty_interface.c997
-rw-r--r--src/host/osmocon/.gitignore10
-rw-r--r--src/host/osmocon/COPYING339
-rw-r--r--src/host/osmocon/Makefile.am21
-rw-r--r--src/host/osmocon/configure.ac25
-rwxr-xr-xsrc/host/osmocon/git-version-gen151
-rwxr-xr-xsrc/host/osmocon/memdump_convert.pl29
-rw-r--r--src/host/osmocon/osmocon.c1507
-rw-r--r--src/host/osmocon/osmoload.c198
-rw-r--r--src/host/osmocon/tpu_debug.c138
-rwxr-xr-xsrc/host/rita_pll/rita_pll.pl111
-rw-r--r--src/host/rita_pll/rita_pll.txt3625
-rw-r--r--src/host/rita_pll/rita_pll_notes.txt8
-rw-r--r--src/shared/libosmocore/.gitignore30
-rw-r--r--src/shared/libosmocore/COPYING339
-rw-r--r--src/shared/libosmocore/Makefile.am14
-rw-r--r--src/shared/libosmocore/configure.in72
-rwxr-xr-xsrc/shared/libosmocore/git-version-gen151
-rw-r--r--src/shared/libosmocore/include/Makefile.am1
-rw-r--r--src/shared/libosmocore/include/osmocom/Makefile.am5
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/Makefile.am3
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h54
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/Makefile.am4
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/buffer.h102
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/command.h349
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/logging.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/telnet_interface.h40
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vector.h64
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vty.h159
-rw-r--r--src/shared/libosmocore/include/osmocore/Makefile.am13
-rw-r--r--src/shared/libosmocore/include/osmocore/bitvec.h75
-rw-r--r--src/shared/libosmocore/include/osmocore/comp128.h22
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm0808.h43
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm48.h36
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm48_ie.h107
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm_utils.h117
-rw-r--r--src/shared/libosmocore/include/osmocore/gsmtap.h72
-rw-r--r--src/shared/libosmocore/include/osmocore/gsmtap_util.h21
-rw-r--r--src/shared/libosmocore/include/osmocore/linuxlist.h360
-rw-r--r--src/shared/libosmocore/include/osmocore/logging.h135
-rw-r--r--src/shared/libosmocore/include/osmocore/mncc.h71
-rw-r--r--src/shared/libosmocore/include/osmocore/msgb.h165
-rw-r--r--src/shared/libosmocore/include/osmocore/plugin.h6
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/Makefile.am3
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h1243
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h188
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h126
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h303
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h516
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h713
-rw-r--r--src/shared/libosmocore/include/osmocore/rate_ctr.h81
-rw-r--r--src/shared/libosmocore/include/osmocore/rsl.h32
-rw-r--r--src/shared/libosmocore/include/osmocore/rxlev_stat.h22
-rw-r--r--src/shared/libosmocore/include/osmocore/select.h22
-rw-r--r--src/shared/libosmocore/include/osmocore/signal.h15
-rw-r--r--src/shared/libosmocore/include/osmocore/statistics.h31
-rw-r--r--src/shared/libosmocore/include/osmocore/talloc.h192
-rw-r--r--src/shared/libosmocore/include/osmocore/timer.h72
-rw-r--r--src/shared/libosmocore/include/osmocore/tlv.h245
-rw-r--r--src/shared/libosmocore/include/osmocore/utils.h20
-rw-r--r--src/shared/libosmocore/include/osmocore/write_queue.h46
-rw-r--r--src/shared/libosmocore/libosmocore.pc.in11
-rw-r--r--src/shared/libosmocore/libosmovty.pc.in11
-rw-r--r--src/shared/libosmocore/m4/DUMMY1
-rw-r--r--src/shared/libosmocore/src/Makefile.am (renamed from src/Makefile.am)0
-rw-r--r--src/shared/libosmocore/src/bitvec.c (renamed from src/bitvec.c)0
-rw-r--r--src/shared/libosmocore/src/comp128.c (renamed from src/comp128.c)0
-rw-r--r--src/shared/libosmocore/src/gprs_cipher_core.c (renamed from src/gprs_cipher_core.c)0
-rw-r--r--src/shared/libosmocore/src/gsm0808.c (renamed from src/gsm0808.c)0
-rw-r--r--src/shared/libosmocore/src/gsm48.c (renamed from src/gsm48.c)0
-rw-r--r--src/shared/libosmocore/src/gsm48_ie.c (renamed from src/gsm48_ie.c)0
-rw-r--r--src/shared/libosmocore/src/gsm_utils.c (renamed from src/gsm_utils.c)0
-rw-r--r--src/shared/libosmocore/src/gsmtap_util.c (renamed from src/gsmtap_util.c)0
-rw-r--r--src/shared/libosmocore/src/logging.c (renamed from src/logging.c)0
-rw-r--r--src/shared/libosmocore/src/msgb.c (renamed from src/msgb.c)0
-rw-r--r--src/shared/libosmocore/src/plugin.c (renamed from src/plugin.c)0
-rw-r--r--src/shared/libosmocore/src/rate_ctr.c (renamed from src/rate_ctr.c)0
-rw-r--r--src/shared/libosmocore/src/rsl.c (renamed from src/rsl.c)0
-rw-r--r--src/shared/libosmocore/src/rxlev_stat.c (renamed from src/rxlev_stat.c)0
-rw-r--r--src/shared/libosmocore/src/select.c (renamed from src/select.c)0
-rw-r--r--src/shared/libosmocore/src/signal.c (renamed from src/signal.c)0
-rw-r--r--src/shared/libosmocore/src/statistics.c (renamed from src/statistics.c)0
-rw-r--r--src/shared/libosmocore/src/talloc.c (renamed from src/talloc.c)0
-rw-r--r--src/shared/libosmocore/src/timer.c (renamed from src/timer.c)0
-rw-r--r--src/shared/libosmocore/src/tlv_parser.c (renamed from src/tlv_parser.c)0
-rw-r--r--src/shared/libosmocore/src/utils.c (renamed from src/utils.c)0
-rw-r--r--src/shared/libosmocore/src/vty/Makefile.am (renamed from src/vty/Makefile.am)0
-rw-r--r--src/shared/libosmocore/src/vty/buffer.c (renamed from src/vty/buffer.c)0
-rw-r--r--src/shared/libosmocore/src/vty/command.c (renamed from src/vty/command.c)0
-rw-r--r--src/shared/libosmocore/src/vty/logging_vty.c (renamed from src/vty/logging_vty.c)0
-rw-r--r--src/shared/libosmocore/src/vty/telnet_interface.c (renamed from src/vty/telnet_interface.c)0
-rw-r--r--src/shared/libosmocore/src/vty/utils.c (renamed from src/vty/utils.c)0
-rw-r--r--src/shared/libosmocore/src/vty/vector.c (renamed from src/vty/vector.c)0
-rw-r--r--src/shared/libosmocore/src/vty/vty.c (renamed from src/vty/vty.c)0
-rw-r--r--src/shared/libosmocore/src/write_queue.c (renamed from src/write_queue.c)0
-rw-r--r--src/shared/libosmocore/tests/Makefile.am3
-rw-r--r--src/shared/libosmocore/tests/sms/Makefile.am5
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.c47
-rw-r--r--src/shared/libosmocore/tests/timer/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.c77
-rwxr-xr-xsrc/shared/update-libosmocore.sh3
-rw-r--r--src/target/firmware/.gitignore9
-rw-r--r--src/target/firmware/COPYING339
-rw-r--r--src/target/firmware/Makefile41
-rw-r--r--src/target/firmware/Makefile.inc183
-rw-r--r--src/target/firmware/abb/twl3025.c356
-rw-r--r--src/target/firmware/apps/compal_dsp_dump/main.c74
-rw-r--r--src/target/firmware/apps/compal_dump/main.c93
-rw-r--r--src/target/firmware/apps/hello_world/main.c137
-rw-r--r--src/target/firmware/apps/l1test/main.c288
-rw-r--r--src/target/firmware/apps/layer1/main.c173
-rw-r--r--src/target/firmware/apps/loader/main.c225
-rw-r--r--src/target/firmware/apps/loader/protocol.h6
-rw-r--r--src/target/firmware/board/common/calypso_pwl.S21
-rw-r--r--src/target/firmware/board/common/calypso_uart.S92
-rw-r--r--src/target/firmware/board/common/compal_osmoload.lds98
-rw-r--r--src/target/firmware/board/common/compal_ramload.lds103
-rw-r--r--src/target/firmware/board/common/compal_ramload_start.S163
-rw-r--r--src/target/firmware/board/common/rffe_compal_dualband.c80
-rw-r--r--src/target/firmware/board/common/rffe_gta0x_triband.c96
-rw-r--r--src/target/firmware/board/compal_e88/init.c132
-rw-r--r--src/target/firmware/board/compal_e99/init.c130
-rw-r--r--src/target/firmware/board/gta0x/init.c132
-rw-r--r--src/target/firmware/board/manifest.c7
-rw-r--r--src/target/firmware/calypso/Makefile4
-rw-r--r--src/target/firmware/calypso/arm.c26
-rw-r--r--src/target/firmware/calypso/backlight.c67
-rw-r--r--src/target/firmware/calypso/clock.c200
-rw-r--r--src/target/firmware/calypso/dma.c44
-rw-r--r--src/target/firmware/calypso/dsp.c465
-rw-r--r--src/target/firmware/calypso/dsp_bootcode.c9
-rw-r--r--src/target/firmware/calypso/dsp_dumpcode.c45
-rw-r--r--src/target/firmware/calypso/dsp_params.c94
-rw-r--r--src/target/firmware/calypso/du.c51
-rw-r--r--src/target/firmware/calypso/i2c.c123
-rw-r--r--src/target/firmware/calypso/irq.c266
-rw-r--r--src/target/firmware/calypso/keypad.c168
-rw-r--r--src/target/firmware/calypso/misc.c60
-rw-r--r--src/target/firmware/calypso/rtc.c83
-rw-r--r--src/target/firmware/calypso/spi.c141
-rw-r--r--src/target/firmware/calypso/timer.c156
-rw-r--r--src/target/firmware/calypso/tpu.c346
-rw-r--r--src/target/firmware/calypso/tsp.c121
-rw-r--r--src/target/firmware/calypso/uart.c440
-rw-r--r--src/target/firmware/calypso/uwire.c136
-rw-r--r--src/target/firmware/comm/Makefile5
-rw-r--r--src/target/firmware/comm/msgb.c74
-rw-r--r--src/target/firmware/comm/sercomm.c277
-rw-r--r--src/target/firmware/comm/sercomm_cons.c140
-rw-r--r--src/target/firmware/comm/timer.c213
-rw-r--r--src/target/firmware/display/display.c20
-rw-r--r--src/target/firmware/display/font_r8x8.cbin0 -> 50349 bytes
-rw-r--r--src/target/firmware/display/font_r8x8_horiz.c261
-rw-r--r--src/target/firmware/display/ssd1783.c257
-rw-r--r--src/target/firmware/display/st7558.c123
-rw-r--r--src/target/firmware/flash/cfi_flash.c436
-rw-r--r--src/target/firmware/include/abb/twl3025.h136
-rw-r--r--src/target/firmware/include/arm.h7
-rw-r--r--src/target/firmware/include/arpa/inet.h2
-rw-r--r--src/target/firmware/include/asm/assembler.h113
-rw-r--r--src/target/firmware/include/asm/atomic.h106
-rw-r--r--src/target/firmware/include/asm/bitops.h225
-rw-r--r--src/target/firmware/include/asm/div64.h48
-rw-r--r--src/target/firmware/include/asm/linkage.h18
-rw-r--r--src/target/firmware/include/asm/ptrace.h128
-rw-r--r--src/target/firmware/include/asm/swab.h45
-rw-r--r--src/target/firmware/include/asm/system.h123
-rw-r--r--src/target/firmware/include/board.h6
-rw-r--r--src/target/firmware/include/byteorder.h79
-rw-r--r--src/target/firmware/include/calypso/backlight.h10
-rw-r--r--src/target/firmware/include/calypso/clock.h67
-rw-r--r--src/target/firmware/include/calypso/dma.h6
-rw-r--r--src/target/firmware/include/calypso/dsp.h35
-rw-r--r--src/target/firmware/include/calypso/dsp_api.h1560
-rw-r--r--src/target/firmware/include/calypso/du.h32
-rw-r--r--src/target/firmware/include/calypso/irq.h49
-rw-r--r--src/target/firmware/include/calypso/l1_environment.h365
-rw-r--r--src/target/firmware/include/calypso/misc.h8
-rw-r--r--src/target/firmware/include/calypso/rtc.h6
-rw-r--r--src/target/firmware/include/calypso/timer.h25
-rw-r--r--src/target/firmware/include/calypso/tpu.h122
-rw-r--r--src/target/firmware/include/calypso/tsp.h30
-rw-r--r--src/target/firmware/include/calypso/uart.h32
-rw-r--r--src/target/firmware/include/cfi_flash.h68
-rw-r--r--src/target/firmware/include/comm/msgb.h161
-rw-r--r--src/target/firmware/include/comm/sercomm.h57
-rw-r--r--src/target/firmware/include/comm/sercomm_cons.h10
-rw-r--r--src/target/firmware/include/comm/timer.h76
-rw-r--r--src/target/firmware/include/console.h20
-rw-r--r--src/target/firmware/include/ctors.h16
-rw-r--r--src/target/firmware/include/ctype.h54
-rw-r--r--src/target/firmware/include/debug.h31
-rw-r--r--src/target/firmware/include/defines.h21
-rw-r--r--src/target/firmware/include/delay.h7
-rw-r--r--src/target/firmware/include/display.h47
-rw-r--r--src/target/firmware/include/display/ssd1783.h56
-rw-r--r--src/target/firmware/include/i2c.h7
-rw-r--r--src/target/firmware/include/keypad.h66
-rw-r--r--src/target/firmware/include/layer1/afc.h18
-rw-r--r--src/target/firmware/include/layer1/agc.h7
-rw-r--r--src/target/firmware/include/layer1/async.h38
-rw-r--r--src/target/firmware/include/layer1/avg.h23
-rw-r--r--src/target/firmware/include/layer1/l23_api.h15
-rw-r--r--src/target/firmware/include/layer1/mframe_sched.h58
-rw-r--r--src/target/firmware/include/layer1/rfch.h9
-rw-r--r--src/target/firmware/include/layer1/sched_gsmtime.h24
-rw-r--r--src/target/firmware/include/layer1/sync.h171
-rw-r--r--src/target/firmware/include/layer1/tdma_sched.h62
-rw-r--r--src/target/firmware/include/layer1/tpu_window.h23
-rw-r--r--src/target/firmware/include/manifest.h10
-rw-r--r--src/target/firmware/include/memory.h28
-rw-r--r--src/target/firmware/include/rf/trf6151.h50
-rw-r--r--src/target/firmware/include/rffe.h19
-rw-r--r--src/target/firmware/include/spi.h7
-rw-r--r--src/target/firmware/include/stdint.h29
-rw-r--r--src/target/firmware/include/stdio.h52
-rw-r--r--src/target/firmware/include/string.h12
-rw-r--r--src/target/firmware/include/swab.h297
-rw-r--r--src/target/firmware/include/uwire.h7
-rw-r--r--src/target/firmware/layer1/Makefile8
-rw-r--r--src/target/firmware/layer1/afc.c130
-rw-r--r--src/target/firmware/layer1/agc.c62
-rw-r--r--src/target/firmware/layer1/async.c95
-rw-r--r--src/target/firmware/layer1/avg.c57
-rw-r--r--src/target/firmware/layer1/init.c74
-rw-r--r--src/target/firmware/layer1/l23_api.c395
-rw-r--r--src/target/firmware/layer1/mframe_sched.c370
-rw-r--r--src/target/firmware/layer1/prim_fbsb.c572
-rw-r--r--src/target/firmware/layer1/prim_pm.c148
-rw-r--r--src/target/firmware/layer1/prim_rach.c136
-rw-r--r--src/target/firmware/layer1/prim_rx_nb.c199
-rw-r--r--src/target/firmware/layer1/prim_tx_nb.c209
-rw-r--r--src/target/firmware/layer1/rfch.c142
-rw-r--r--src/target/firmware/layer1/sched_gsmtime.c119
-rw-r--r--src/target/firmware/layer1/sync.c367
-rw-r--r--src/target/firmware/layer1/tdma_sched.c185
-rw-r--r--src/target/firmware/layer1/tpu_window.c133
-rw-r--r--src/target/firmware/lib/Makefile7
-rw-r--r--src/target/firmware/lib/bitops.h33
-rw-r--r--src/target/firmware/lib/changebit.S21
-rw-r--r--src/target/firmware/lib/clearbit.S22
-rw-r--r--src/target/firmware/lib/console.c190
-rw-r--r--src/target/firmware/lib/copy_template.S255
-rw-r--r--src/target/firmware/lib/ctors.c15
-rw-r--r--src/target/firmware/lib/ctype.c34
-rw-r--r--src/target/firmware/lib/div64.S200
-rw-r--r--src/target/firmware/lib/lib1funcs.S334
-rw-r--r--src/target/firmware/lib/memcpy.S59
-rw-r--r--src/target/firmware/lib/memset.S80
-rw-r--r--src/target/firmware/lib/printf.c19
-rw-r--r--src/target/firmware/lib/setbit.S22
-rw-r--r--src/target/firmware/lib/string.c50
-rw-r--r--src/target/firmware/lib/testchangebit.S18
-rw-r--r--src/target/firmware/lib/testclearbit.S18
-rw-r--r--src/target/firmware/lib/testsetbit.S18
-rw-r--r--src/target/firmware/lib/vsprintf.c847
-rw-r--r--src/target/firmware/rf/trf6151.c502
-rw-r--r--src/target_dsp/.gitignore4
-rw-r--r--src/target_dsp/calypso/Makefile7
-rwxr-xr-xsrc/target_dsp/calypso/bin2cfile.py50
-rw-r--r--src/target_dsp/calypso/bl_stage3.S142
-rw-r--r--src/target_dsp/calypso/dsp_dump.lds22
-rwxr-xr-xsrc/target_dsp/calypso/dump2coff.py261
-rw-r--r--src/target_dsp/calypso/ida/README.txt73
-rw-r--r--src/target_dsp/calypso/ida/ndb.h294
-rw-r--r--src/target_dsp/calypso/ida/tms320c54.cfg136
-rw-r--r--src/wireshark/gsmtap.patch445
328 files changed, 58434 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 00000000..5151bfb5
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,90 @@
+
+# this is not really used as we don't do 'make install'. You can still specify
+# it in case you _want_ to manually 'make install' the target libosmocore.
+CROSS_INST_PREFIX=/usr/local/stow/osmocom-bb/arm-elf
+
+# this is the prefix of your cross-toolchain programs
+CROSS_TOOL_PREFIX=arm-elf-
+
+TOPDIR=$(shell pwd)
+OSMOCORE_CONFIGURE_ENV= LIBOSMOCORE_LIBS=$(TOPDIR)/shared/libosmocore/build-host/src/.libs/libosmocore.a \
+ LIBOSMOVTY_LIBS=$(TOPDIR)/shared/libosmocore/build-host/src/vty/.libs/libosmovty.a \
+ LIBOSMOCORE_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include \
+ LIBOSMOVTY_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include
+
+all: libosmocore-host libosmocore-target layer23 osmocon firmware
+
+libosmocore-host: shared/libosmocore/build-host/src/.libs/libosmocore.la
+
+shared/libosmocore/build-host:
+ mkdir $@
+
+shared/libosmocore/configure: shared/libosmocore/configure.in
+ cd shared/libosmocore && autoreconf -i
+
+shared/libosmocore/build-host/Makefile: shared/libosmocore/configure shared/libosmocore/build-host
+ cd shared/libosmocore/build-host && ../configure
+
+shared/libosmocore/build-host/src/.libs/libosmocore.la: shared/libosmocore/build-host/Makefile
+ cd shared/libosmocore/build-host && make
+
+
+libosmocore-target: shared/libosmocore/build-target/src/.libs/libosmocore.a
+
+shared/libosmocore/build-target:
+ mkdir $@
+
+shared/libosmocore/build-target/Makefile: shared/libosmocore/configure shared/libosmocore/build-target
+ cd shared/libosmocore/build-target && ../configure \
+ --host=arm-elf-linux --disable-vty \
+ --disable-shared --disable-talloc --disable-tests \
+ CC="$(CROSS_TOOL_PREFIX)gcc" CFLAGS="-Os -ffunction-sections -I../../../../target/firmware/include"
+
+shared/libosmocore/build-target/src/.libs/libosmocore.a: shared/libosmocore/build-target/Makefile
+ cd shared/libosmocore/build-target && make
+
+
+.PHONY: osmocon
+osmocon: host/osmocon/osmocon
+
+host/osmocon/configure: host/osmocon/configure.ac
+ cd host/osmocon && autoreconf -i
+
+host/osmocon/Makefile: host/osmocon/configure
+ cd host/osmocon && $(OSMOCORE_CONFIGURE_ENV) ./configure
+
+host/osmocon/osmocon: host/osmocon/Makefile libosmocore-host
+ make -C host/osmocon
+
+
+.PHONY: layer23
+layer23: host/layer23/layer23
+
+host/layer23/configure: host/layer23/configure.ac
+ cd host/layer23 && autoreconf -i
+
+host/layer23/Makefile: host/layer23/configure
+ cd host/layer23 && $(OSMOCORE_CONFIGURE_ENV) ./configure
+
+host/layer23/layer23: host/layer23/Makefile libosmocore-host
+ make -C host/layer23
+
+
+.PHONY: firmware
+firmware: libosmocore-target
+ make -C target/firmware CROSS_COMPILE=$(CROSS_TOOL_PREFIX)
+
+
+clean:
+ make -C shared/libosmocore/build-host $@
+ make -C shared/libosmocore/build-target $@
+ make -C host/layer23 $@
+ make -C host/osmocon $@
+ make -C target/firmware $@
+
+distclean:
+ rm -rf shared/libosmocore/build-host
+ rm -rf shared/libosmocore/build-target
+ make -C host/layer23 $@
+ make -C host/osmocon $@
+ make -C target/firmware $@
diff --git a/src/README.building b/src/README.building
new file mode 100644
index 00000000..32d03c95
--- /dev/null
+++ b/src/README.building
@@ -0,0 +1,20 @@
+== How to build OsmocomBB? ==
+
+=== Prerequisites ===
+
+We assume you are building on a GNU/Linux host system such as Debian
+GNU/Linux. Windows builds have been reported successfully using the
+Cygwin environment, but we do not officially support this.
+
+ # Get a GNU toolchain (gcc/binutils) for ARM (e.g. from http://gnuarm.com/)
+ # Set your path to include the arm-elf-* executables of your toolchain
+ # call 'make' in this (the src) subdirectory
+
+=== Details ===
+
+The master Makefile will build
+
+ * libosmocore for the host (x86 or whatever you use)
+ * libosmocore for the target (ARM)
+ * osmocon and layer23 executables for the host (linking libosmocore)
+ * the actual target firmware images (in src/target/firmware/board/*/*.bin)
diff --git a/src/README.development b/src/README.development
new file mode 100644
index 00000000..393b0bd9
--- /dev/null
+++ b/src/README.development
@@ -0,0 +1,57 @@
+= Contributing to OsmocomBB development =
+
+Feel free to help us by extending the code. Always make sure to
+send back all your patches to the baseband-devel@lists.osmocom.org
+mailing list - Free Software is all about sharing.
+
+== src/shared/libosmocore ==
+
+is a library of various utility routines, including linked lists,
+message buffers, bit-vectors, memory allocator, signals, select loop
+handling, timers - as well as some more specifically GSM related things
+like a TLV parser, a Comp128V1 implementation and utility functions for
+RSL (TS 08.58) and CC/MM/RR (TS 04.08).
+
+libosmocore is maintained in git://git.osmocom.org/libosmocore.git, so
+
+ DO NOT DIRECTLY COMMIT TO libosmocore IN THIS REPOSITORY!
+
+We simply maintain a copy (synchronized by git-subtree) in this
+repository for the ease of building and to make sure everyone is using
+the proper/compatible version of libosmocore
+
+Please note, whatever you add to libosmocore will need to build as a
+Linux userspace program (using glibc) just as well as on the OsmocomBB
+embedded target without OS. So please refrain from using fancy
+functions.
+
+
+== src/target/firmware ==
+
+The firmware is what we build for the actual target (phone). It was
+written with some idea of modularity in mind, i.e. we have
+
+ * Ti Calypso specific code in calypso/
+ * Analog Baseband code in abb/
+ * RF Mixer code in rf/
+ * Layer1 code in layer1/
+ * NOR flash handling in flash/
+ * LCD display handlin in display/
+ * minimal C-Library code in lib/
+ * communications utility routines in comm/
+ * Board (phone model/family) specific code in board/
+ * board/compal_e88 is the Motorola C115-C124 family
+ * board/compal_e99 is the Motorola C155 family
+ * Applications (each app builds one firmware image) in apps/
+
+
+== src/target_dsp/calypso ==
+
+This is where we keep some (assembly) code that we wrote for
+the DSP that is part of the Caylypso DBB.
+
+== host/layer23 ==
+
+The Layer2 (LAPDm / TS 04.06) and Layer3 (CC/MM/RR / 04.08)
+implementations, as they are growing.
+
diff --git a/src/host/calypso_pll/pll.pl b/src/host/calypso_pll/pll.pl
new file mode 100755
index 00000000..52c91319
--- /dev/null
+++ b/src/host/calypso_pll/pll.pl
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+my $f_in = 26*1000*1000;
+
+for (my $mult = 1; $mult < 31; $mult++) {
+ for (my $div = 0; $div < 3; $div++) {
+ my $fout = $f_in * ($mult / ($div+1));
+ printf("%03.1f MHz (mult=%2u, div=%1u)\n", $fout/(1000*1000), $mult, $div);
+ }
+}
diff --git a/src/host/gsm48-andreas/issues.txt b/src/host/gsm48-andreas/issues.txt
new file mode 100644
index 00000000..ba7bb827
--- /dev/null
+++ b/src/host/gsm48-andreas/issues.txt
@@ -0,0 +1,99 @@
+Location updating procedure:
+
+When SIM is "NOT UPDATED", there is no valid LAI in SIM. LOCATION UPDATING
+REQUEST message requires LAI. How is an IMSI attach possible, if no LAI
+exists?
+We have three conditions:
+- LAI in SIM is valid
+- LAI in SIM exists (last stored) but marked invalid.
+- No LAI in SIM (brand new one)
+See Table 10.5.3 TS 04.08 and try to understand the riddle...
+
+
+OpenBSC:
+If tx_setup fails during process, the msg must be freed to avoid memory leak.
+
+
+OpenBSC:
+Must use *_LOC_PRN_S_LU on protocol side.
+Or it uses *_LOC_PUN_S_LU to hide private network type (maybe some cfg option)
+
+
+LCR:
+Also LCR must use correct location.
+For MS support, it must use *_LOC_USER
+
+
+Measurements:
+How do we send MEASUREMENT RESULTS to RSL? (maybe RSL_MT_MEAS_RES)
+what triggers the sending? Or do we just send it from time to time to layer 1
+where it is stored and sent when the time is right? (Then we might get
+something like RSL_MT_MEAS_CNF, so we can send the next one, if we have new
+measurements available, otherwhise the L1 will use the old measurement results
+and resends them without confirming it.)
+
+
+RACH:
+I need some RACH primitives like:
+- RACH request (tx_ph_rach_req already exists) including value and offset of
+ RACH slot from now.
+- RACH confirm, the RACK has been sent (with timeslot where is was sent)
+- RACH cancel request:
+ Since RACH request must be queued in layer 1 until it the right slot is
+ reached, I need to tell L1 to cancel it. Whenever I receive a confirm, I
+ schedule the next one until the maximum number has been transmitted or until
+ an IMMEDIATE ASSIGNMENT is received on CCCH.
+Any better idea? Read GSM 04.08 clause 3.3.1.1.2 and 3.3.1.1.3 for more info.
+
+
+RACH request on RSL:
+Does it make sense to put RACH request to the set of RSLms primitives?
+(i.e. RSL_MT_CHAN_REQ, not RQD!, RSL_MT_CHAN_CNF, RSL_MT_CHAN_CAN)
+What do you say?
+
+
+I need to change storage of system informations when searching other cells.
+I prefer two modes:
+- collecting all SI on serving cell, collecting only relevant data of other cell
+- collecting all SI on serving cell and other cells, storing them for each
+ frequency (netmonitor)
+
+How do I (radio ressource) know about the channel layout on BCCH? This info is
+requred to select correct delays and timings.
+-> i think i got it from system informations^
+
+
+How do I return after L1CTL_DM_EST_REQ back to idle mode?
+Using l1ctl_tx_ccch_req?
+
+
+mncc.h of openbsc / layer23:
+What about putting all (except call structure) to osmocore?
+
+
+bsic???????
+
+
+How do I stop scanning frequencies?
+The scanning process may currently restart scanning, when process is running.
+
+
+It is absolutely importaint for messages not beeing lost between layers.
+What about the serial link between mobile and PC?
+-> There must be a security layer!
+
+One solution: Store all radio action (DM / CCCH / power scan) and start timer.
+Send request again, if timer expires.
+-> Also send it again, if L1 resets (restart of phone).
+We MUST at least have CRCxx on serial interface.
+
+
+power measurements sometimes does not return all measurements. it stops,
+especially if a ccch was requested before. this is not good. power measurement
+must stop all other layer 1 processes!
+
+
+
+
+
+
diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore
new file mode 100644
index 00000000..d3594665
--- /dev/null
+++ b/src/host/layer23/.gitignore
@@ -0,0 +1,14 @@
+Makefile
+Makefile.in
+aclocal.m4
+configure
+missing
+*.o
+depcomp
+config.*
+*.sw?
+*.deps
+layer2
+install-sh
+autom4te.cache
+*.a
diff --git a/src/host/layer23/COPYING b/src/host/layer23/COPYING
new file mode 100644
index 00000000..d511905c
--- /dev/null
+++ b/src/host/layer23/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src/host/layer23/Makefile.am b/src/host/layer23/Makefile.am
new file mode 100644
index 00000000..bc3910fa
--- /dev/null
+++ b/src/host/layer23/Makefile.am
@@ -0,0 +1,3 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+
+SUBDIRS = include src
diff --git a/src/host/layer23/README b/src/host/layer23/README
new file mode 100644
index 00000000..dd598234
--- /dev/null
+++ b/src/host/layer23/README
@@ -0,0 +1,42 @@
+= OsmocomBB layer23 architecture =
+
+layer23 is an (incomplete) MS-side implementation of the L2 and L3 GSM
+protocols as described in GSM TS 04.06, 04.08 and others.
+
+== Interfaces ==
+
+L1 (on the phone) uses the L1CTL protocol to talk with layer23 (on the PC).
+
+L2 (inside layer23) uses the RSLms protocol to talk with the L3 (inside layer23)
+
+
+=== RSLms ===
+
+RSLms is modeled after the GSM TS 08.58 Radio Subsystem Link protocol. Despite
+being designed for the network side, RSL seems a good match for the L2/L3
+interface inside a MS, too.
+
+At least the RLL (Radio Link Layer) part of RSL is 100% as applicable to the MS
+side as it is for the ntwork side.
+
+==== Lower interface (L2 to RSLms) ====
+
+Layer2 calls rslms_sendmsg() with a msgb that has the msgb->l2h pointing to a
+RSL header (struct abis_rsl_common_hdr).
+
+==== Upper interface (L3 to RSLms) ====
+
+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.
+
+
+=== LAPDm ===
+
+LAPDm is the GSM TS 04.06 protocol
+
+The lower interface (to L1) is using L1CTL
+
+The upper interface (to L3) is using RSLms
diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac
new file mode 100644
index 00000000..036161c0
--- /dev/null
+++ b/src/host/layer23/configure.ac
@@ -0,0 +1,28 @@
+dnl Process this file with autoconf to produce a configure script
+AC_INIT
+
+AM_INIT_AUTOMAKE(layer23, 0.0.0)
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl checks for programs
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+dnl checks for libraries
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
+
+dnl checks for header files
+AC_HEADER_STDC
+
+dnl Checks for typedefs, structures and compiler characteristics
+
+AC_OUTPUT(
+ src/Makefile
+ include/Makefile
+ include/osmocom/Makefile
+ Makefile)
diff --git a/src/host/layer23/include/Makefile.am b/src/host/layer23/include/Makefile.am
new file mode 100644
index 00000000..ca774b6b
--- /dev/null
+++ b/src/host/layer23/include/Makefile.am
@@ -0,0 +1,2 @@
+noinst_HEADERS = l1a_l23_interface.h
+SUBDIRS = osmocom
diff --git a/src/host/layer23/include/l1a_l23_interface.h b/src/host/layer23/include/l1a_l23_interface.h
new file mode 120000
index 00000000..2bbc9679
--- /dev/null
+++ b/src/host/layer23/include/l1a_l23_interface.h
@@ -0,0 +1 @@
+../../../../include/l1a_l23_interface.h \ No newline at end of file
diff --git a/src/host/layer23/include/osmocom/Makefile.am b/src/host/layer23/include/osmocom/Makefile.am
new file mode 100644
index 00000000..86537aa5
--- /dev/null
+++ b/src/host/layer23/include/osmocom/Makefile.am
@@ -0,0 +1,2 @@
+noinst_HEADERS = l1ctl.h osmocom_data.h lapdm.h rslms.h layer3.h \
+ gsmtap_util.h logging.h
diff --git a/src/host/layer23/include/osmocom/file.h b/src/host/layer23/include/osmocom/file.h
new file mode 100644
index 00000000..249064d9
--- /dev/null
+++ b/src/host/layer23/include/osmocom/file.h
@@ -0,0 +1,80 @@
+#ifndef _OSMOCOM_FILE_H
+#define _OSMOCOM_FILE_H
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#define OSMOCOM_CONFDIR "/etc/osmocom/"
+#define OSMOCOM_PERM 0644
+#define OSMOCOM_FILE FILE
+static inline OSMOCOM_FILE *osmocom_fopen(const char *name, const char *mode)
+{
+ char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1];
+
+ if (mode[0] != 'r') {
+ int rc;
+
+ rc = mkdir(OSMOCOM_CONFDIR, OSMOCOM_PERM);
+ if (rc < 1 && errno != EEXIST)
+ return NULL;
+ }
+
+ strcpy(filename, OSMOCOM_CONFDIR);
+ strcat(filename, name);
+
+ return fopen(filename, mode);
+}
+#define osmocom_fread fread
+#define osmocom_fgets fgets
+#define osmocom_fwrite fwrite
+#define osmocom_feof feof
+#define osmocom_fclose fclose
+static inline int OSMOCOM_UNLINK(const char *name)
+{
+ char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1];
+
+ strcpy(filename, OSMOCOM_CONFDIR);
+ strcat(filename, name);
+
+ return unlink(filename);
+}
+static inline int OSMOCOM_LINK(const char *oldname, const char *newname)
+{
+ char oldfilename[strlen(OSMOCOM_CONFDIR) + strlen(oldname) + 1];
+ char newfilename[strlen(OSMOCOM_CONFDIR) + strlen(newname) + 1];
+
+ strcpy(oldfilename, OSMOCOM_CONFDIR);
+ strcat(oldfilename, oldname);
+ strcpy(newfilename, OSMOCOM_CONFDIR);
+ strcat(newfilename, newname);
+ return link(oldfilename, newfilename);
+}
+static inline int OSMOCOM_MKSTEMP(char *name)
+{
+ char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1];
+ int rc;
+
+ strcpy(filename, OSMOCOM_CONFDIR);
+ strcat(filename, name);
+
+ rc = mkstemp(filename);
+
+ memcpy(name + strlen(name) - 6, filename + strlen(filename) - 6, 6);
+
+ return rc;
+}
+static inline int OSMOCOM_CHMOD(const char *name, mode_t mode)
+{
+ char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1];
+
+ strcpy(filename, OSMOCOM_CONFDIR);
+ strcat(filename, name);
+
+ return chmod(filename, mode);
+}
+
+#endif /* _OSMOCOM_FILE_H */
diff --git a/src/host/layer23/include/osmocom/gsm322.h b/src/host/layer23/include/osmocom/gsm322.h
new file mode 100755
index 00000000..d9cd2261
--- /dev/null
+++ b/src/host/layer23/include/osmocom/gsm322.h
@@ -0,0 +1,199 @@
+#ifndef _GSM322_H
+#define _GSM322_H
+
+/* 4.3.1.1 List of states for PLMN slection process (automatic mode) */
+#define GSM322_A0_NULL 0
+#define GSM322_A1_TRYING_RPLMN 1
+#define GSM322_A2_ON_PLMN 2
+#define GSM322_A3_TRYING_PLMN 3
+#define GSM322_A4_WAIT_FOR_PLMN 4
+#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) */
+#define GSM322_M0_NULL 0
+#define GSM322_M1_TRYING_RPLMN 1
+#define GSM322_M2_ON_PLMN 2
+#define GSM322_M3_NOT_ON_PLMN 3
+#define GSM322_M4_TRYING_PLMN 4
+#define GSM322_M5_NO_SIM 5
+
+/* 4.3.2 List of states for cell selection process */
+#define GSM322_C0_NULL 0
+#define GSM322_C1_NORMAL_CELL_SEL 1
+#define GSM322_C2_STORED_CELL_SEL 2
+#define GSM322_C3_CAMPED_NORMALLY 3
+#define GSM322_C4_NORMAL_CELL_RESEL 4
+#define GSM322_C5_CHOOSE_CELL 5
+#define GSM322_C6_ANY_CELL_SEL 6
+#define GSM322_C7_CAMPED_ANY_CELL 7
+#define GSM322_C8_ANY_CELL_RESEL 8
+#define GSM322_C9_CHOOSE_ANY_CELL 9
+#define GSM322_PLMN_SEARCH 10
+#define GSM322_HPLMN_SEARCH 11
+
+/* GSM 03.22 events */
+#define GSM322_EVENT_SWITCH_ON 1
+#define GSM322_EVENT_SWITCH_OFF 2
+#define GSM322_EVENT_SIM_INSERT 3
+#define GSM322_EVENT_SIM_REMOVE 4
+#define GSM322_EVENT_REG_SUCCESS 5
+#define GSM322_EVENT_REG_FAILED 6
+#define GSM322_EVENT_ROAMING_NA 7
+#define GSM322_EVENT_INVALID_SIM 8
+#define GSM322_EVENT_NEW_PLMN 9
+#define GSM322_EVENT_ON_PLMN 10
+#define GSM322_EVENT_PLMN_SEARCH_START 11
+#define GSM322_EVENT_PLMN_SEARCH_END 12
+#define GSM322_EVENT_USER_RESEL 13
+#define GSM322_EVENT_PLMN_AVAIL 14
+#define GSM322_EVENT_CHOSE_PLMN 15
+#define GSM322_EVENT_SEL_MANUAL 16
+#define GSM322_EVENT_SEL_AUTO 17
+#define GSM322_EVENT_CELL_FOUND 18
+#define GSM322_EVENT_NO_CELL_FOUND 19
+#define GSM322_EVENT_LEAVE_IDLE 20
+#define GSM322_EVENT_RET_IDLE 21
+#define GSM322_EVENT_CELL_RESEL 22
+#define GSM322_EVENT_SYSINFO 23
+#define GSM322_EVENT_HPLMN_SEARCH 24
+
+enum {
+ PLMN_MODE_MANUAL,
+ PLMN_MODE_AUTO
+};
+
+/* node for each PLMN */
+struct gsm322_plmn_list {
+ struct llist_head entry;
+ uint16_t mcc, mnc;
+ int8_t rxlev_db; /* rx level in real dB */
+ uint8_t cause; /* cause value, if PLMN is not allowed */
+};
+
+/* node for each forbidden LA */
+struct gsm322_la_list {
+ struct llist_head entry;
+ uint16_t mcc, mnc, lac;
+ uint8_t cause;
+};
+
+/* node for each BA-List */
+struct gsm322_ba_list {
+ struct llist_head entry;
+ uint16_t mcc, mnc;
+ /* Band allocation for 1024 frequencies.
+ * First bit of first index is frequency 0.
+ */
+ uint8_t freq[128];
+};
+
+#define GSM322_CS_FLAG_SUPPORT 0x01 /* frequency is supported by radio */
+#define GSM322_CS_FLAG_BA 0x02 /* frequency is part of the current ba */
+#define GSM322_CS_FLAG_POWER 0x04 /* frequency was power scanned */
+#define GSM322_CS_FLAG_SIGNAL 0x08 /* valid signal detected */
+#define GSM322_CS_FLAG_SYSINFO 0x10 /* complete sysinfo received */
+#define GSM322_CS_FLAG_BARRED 0x20 /* cell is barred */
+#define GSM322_CS_FLAG_FORBIDD 0x40 /* cell in list of forbidden LAs */
+#define GSM322_CS_FLAG_TEMP_AA 0x80 /* if temporary available and allowable */
+
+/* Cell selection list */
+struct gsm322_cs_list {
+ uint8_t flags; /* see GSM322_CS_FLAG_* */
+ int8_t rxlev_db; /* rx level in real dB */
+ struct gsm48_sysinfo *sysinfo;
+#if 0
+ int8_t min_db; /* minimum level to enter cell */
+ int8_t max_pwr; /* maximum power to access cell */
+ uint16_t class_barr; /* barred classes */
+ uint16_t mcc, mnc, lac; /* received mcc, mnc, lac */
+#endif
+};
+
+/* PLMN search process */
+struct gsm322_plmn {
+ struct osmocom_ms *ms;
+ int state; /* GSM322_Ax_* or GSM322_Mx_* */
+
+ struct llist_head event_queue; /* event messages */
+ struct llist_head sorted_plmn; /* list of sorted PLMN */
+ struct llist_head forbidden_la; /* forbidden LAs */
+
+ struct timer_list timer;
+
+ int plmn_curr; /* current index in sorted_plmn */
+ uint16_t mcc, mnc; /* current network selected */
+};
+
+/* state of CCCH activation */
+#define GSM322_CCCH_ST_IDLE 0 /* no connection */
+#define GSM322_CCCH_ST_INIT 1 /* initalized */
+#define GSM322_CCCH_ST_SYNC 2 /* got sync */
+#define GSM322_CCCH_ST_DATA 3 /* receiveing data */
+
+struct gsm48_sysinfo;
+/* Cell selection process */
+struct gsm322_cellsel {
+ struct osmocom_ms *ms;
+ int state; /* GSM322_Cx_* */
+
+ struct llist_head event_queue; /* event messages */
+ struct llist_head ba_list; /* BCCH Allocation per PLMN */
+
+ struct timer_list timer;
+
+ uint16_t mcc, mnc; /* current network to search for */
+ struct gsm322_cs_list list[1024]; /* cell selection list per freq. */
+
+ uint8_t powerscan; /* currently scanning for power */
+ uint32_t scan_state; /* special state of current scan */
+ uint8_t ccch_state; /* special state of current ccch */
+ uint16_t arfcn; /* current tuned idle mode arfcn */
+ uint8_t ccch_mode; /* curren CCCH_MODE_* */
+ struct gsm48_sysinfo *si; /* current sysinfo */
+
+ uint8_t selected; /* if a cell is selected */
+ uint16_t sel_arfcn;
+ struct gsm48_sysinfo sel_si; /* copy of selected cell, will update */
+ uint16_t sel_mcc, sel_mnc, sel_lac, sel_id;
+};
+
+/* GSM 03.22 message */
+struct gsm322_msg {
+ int msg_type;
+ uint16_t mcc, mnc;
+ uint8_t sysinfo; /* system information type */
+ uint8_t reject; /* location update reject cause */
+};
+
+#define GSM322_ALLOC_SIZE sizeof(struct gsm322_msg)
+#define GSM322_ALLOC_HEADROOM 0
+
+int gsm322_init(struct osmocom_ms *ms);
+int gsm322_exit(struct osmocom_ms *ms);
+struct msgb *gsm322_msgb_alloc(int msg_type);
+int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg);
+int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg);
+int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg);
+int gsm322_plmn_dequeue(struct osmocom_ms *ms);
+int gsm322_cs_dequeue(struct osmocom_ms *ms);
+int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
+ uint16_t mnc, uint16_t lac, uint8_t cause);
+int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
+ uint16_t mnc, uint16_t lac);
+int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
+ uint16_t lac);
+int gsm322_dump_sorted_plmn(struct osmocom_ms *ms);
+int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
+ void (*print)(void *, const char *, ...), void *priv);
+int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
+ void (*print)(void *, const char *, ...), void *priv);
+int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
+ void (*print)(void *, const char *, ...), void *priv);
+void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro);
+void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro);
+extern const char *plmn_a_state_names[];
+extern const char *plmn_m_state_names[];
+extern const char *cs_state_names[];
+
+#endif /* _GSM322_H */
diff --git a/src/host/layer23/include/osmocom/gsm48_cc.h b/src/host/layer23/include/osmocom/gsm48_cc.h
new file mode 100644
index 00000000..d6ea5756
--- /dev/null
+++ b/src/host/layer23/include/osmocom/gsm48_cc.h
@@ -0,0 +1,18 @@
+#ifndef _GSM48_CC_H
+#define _GSM48_CC_H
+
+struct gsm48_cclayer {
+ struct osmocom_ms *ms;
+
+ struct llist_head mncc_upqueue;
+ int (*mncc_recv)(struct osmocom_ms *, int, void *);
+};
+
+int gsm48_cc_init(struct osmocom_ms *ms);
+int gsm48_cc_exit(struct osmocom_ms *ms);
+int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg);
+int mncc_dequeue(struct osmocom_ms *ms);
+int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg);
+
+#endif /* _GSM48_CC_H */
+
diff --git a/src/host/layer23/include/osmocom/gsm48_mm.h b/src/host/layer23/include/osmocom/gsm48_mm.h
new file mode 100644
index 00000000..22c59835
--- /dev/null
+++ b/src/host/layer23/include/osmocom/gsm48_mm.h
@@ -0,0 +1,227 @@
+#ifndef _GSM48_MM_H
+#define _GSM48_MM_H
+
+/* GSM 04.07 9.2.2 */
+#define GSM48_MMXX_MASK 0xf00
+#define GSM48_MMCC_CLASS 0x100
+#define GSM48_MMSS_CLASS 0x200
+#define GSM48_MMSMS_CLASS 0x300
+#define GSM48_MMCC_EST_REQ 0x110
+#define GSM48_MMCC_EST_IND 0x112
+#define GSM48_MMCC_EST_CNF 0x111
+#define GSM48_MMCC_REL_REQ 0x120
+#define GSM48_MMCC_REL_IND 0x122
+#define GSM48_MMCC_DATA_REQ 0x130
+#define GSM48_MMCC_DATA_IND 0x132
+#define GSM48_MMCC_UNIT_DATA_REQ 0x140
+#define GSM48_MMCC_UNIT_DATA_IND 0x142
+#define GSM48_MMCC_SYNC_IND 0x152
+#define GSM48_MMCC_REEST_REQ 0x160
+#define GSM48_MMCC_REEST_CNF 0x161
+#define GSM48_MMCC_ERR_IND 0x172
+#define GSM48_MMCC_PROMPT_IND 0x182
+#define GSM48_MMCC_PROMPT_REJ 0x184
+#define GSM48_MMSS_EST_REQ 0x210
+#define GSM48_MMSS_EST_IND 0x212
+#define GSM48_MMSS_EST_CNF 0x211
+#define GSM48_MMSS_REL_REQ 0x220
+#define GSM48_MMSS_REL_IND 0x222
+#define GSM48_MMSS_DATA_REQ 0x230
+#define GSM48_MMSS_DATA_IND 0x232
+#define GSM48_MMSS_UNIT_DATA_REQ 0x240
+#define GSM48_MMSS_UNIT_DATA_IND 0x242
+#define GSM48_MMSS_REEST_REQ 0x260
+#define GSM48_MMSS_REEST_CNF 0x261
+#define GSM48_MMSS_ERR_IND 0x272
+#define GSM48_MMSS_PROMPT_IND 0x282
+#define GSM48_MMSS_PROMPT_REJ 0x284
+#define GSM48_MMSMS_EST_REQ 0x310
+#define GSM48_MMSMS_EST_IND 0x312
+#define GSM48_MMSMS_EST_CNF 0x311
+#define GSM48_MMSMS_REL_REQ 0x320
+#define GSM48_MMSMS_REL_IND 0x322
+#define GSM48_MMSMS_DATA_REQ 0x330
+#define GSM48_MMSMS_DATA_IND 0x332
+#define GSM48_MMSMS_UNIT_DATA_REQ 0x340
+#define GSM48_MMSMS_UNIT_DATA_IND 0x342
+#define GSM48_MMSMS_REEST_REQ 0x360
+#define GSM48_MMSMS_REEST_CNF 0x361
+#define GSM48_MMSMS_ERR_IND 0x372
+#define GSM48_MMSMS_PROMPT_IND 0x382
+#define GSM48_MMSMS_PROMPT_REJ 0x384
+
+#define MMXX_ALLOC_SIZE 256
+#define MMXX_ALLOC_HEADROOM 64
+
+/* MMxx-SAP header */
+struct gsm48_mmxx_hdr {
+ int msg_type; /* MMxx_* primitive */
+ uint32_t ref; /* reference to transaction */
+ uint32_t transaction_id; /* transaction identifier */
+ uint8_t emergency; /* emergency type of call */
+ uint8_t cause; /* cause used for release */
+};
+
+/* GSM 6.1.2 */
+#define GSM48_MMR_REG_REQ 0x01
+#define GSM48_MMR_REG_CNF 0x02
+#define GSM48_MMR_NREG_REQ 0x03
+#define GSM48_MMR_NREG_IND 0x04
+
+/* MMR-SAP header */
+struct gsm48_mmr {
+ int msg_type;
+
+ uint8_t cause;
+};
+
+/* GSM 04.07 9.2.1 */
+#define GSM48_MMXX_ST_IDLE 0
+#define GSM48_MMXX_ST_CONN_PEND 1
+#define GSM48_MMXX_ST_DEDICATED 2
+#define GSM48_MMXX_ST_CONN_SUSP 3
+#define GSM48_MMXX_ST_REESTPEND 4
+
+/* GSM 04.08 4.1.2.1 */
+#define GSM48_MM_ST_NULL 0
+#define GSM48_MM_ST_LOC_UPD_INIT 3
+#define GSM48_MM_ST_WAIT_OUT_MM_CONN 5
+#define GSM48_MM_ST_MM_CONN_ACTIVE 6
+#define GSM48_MM_ST_IMSI_DETACH_INIT 7
+#define GSM48_MM_ST_PROCESS_CM_SERV_P 8
+#define GSM48_MM_ST_WAIT_NETWORK_CMD 9
+#define GSM48_MM_ST_LOC_UPD_REJ 10
+#define GSM48_MM_ST_WAIT_RR_CONN_LUPD 13
+#define GSM48_MM_ST_WAIT_RR_CONN_MM_CON 14
+#define GSM48_MM_ST_WAIT_RR_CONN_IMSI_D 15
+#define GSM48_MM_ST_WAIT_REEST 17
+#define GSM48_MM_ST_WAIT_RR_ACTIVE 18
+#define GSM48_MM_ST_MM_IDLE 19
+#define GSM48_MM_ST_WAIT_ADD_OUT_MM_CON 20
+#define GSM48_MM_ST_MM_CONN_ACTIVE_VGCS 21
+#define GSM48_MM_ST_WAIT_RR_CONN_VGCS 22
+#define GSM48_MM_ST_LOC_UPD_PEND 23
+#define GSM48_MM_ST_IMSI_DETACH_PEND 24
+#define GSM48_MM_ST_RR_CONN_RELEASE_NA 25
+
+/* GSM 04.08 4.1.2.1 */
+#define GSM48_MM_SST_NORMAL_SERVICE 1
+#define GSM48_MM_SST_ATTEMPT_UPDATE 2
+#define GSM48_MM_SST_LIMITED_SERVICE 3
+#define GSM48_MM_SST_NO_IMSI 4
+#define GSM48_MM_SST_NO_CELL_AVAIL 5
+#define GSM48_MM_SST_LOC_UPD_NEEDED 6
+#define GSM48_MM_SST_PLMN_SEARCH 7
+#define GSM48_MM_SST_PLMN_SEARCH_NORMAL 8
+#define GSM48_MM_SST_RX_VGCS_NORMAL 9
+#define GSM48_MM_SST_RX_VGCS_LIMITED 10
+
+/* MM events */
+#define GSM48_MM_EVENT_CELL_SELECTED 1
+#define GSM48_MM_EVENT_NO_CELL_FOUND 2
+#define GSM48_MM_EVENT_TIMEOUT_T3210 3
+#define GSM48_MM_EVENT_TIMEOUT_T3211 4
+#define GSM48_MM_EVENT_TIMEOUT_T3212 5
+#define GSM48_MM_EVENT_TIMEOUT_T3213 6
+#define GSM48_MM_EVENT_TIMEOUT_T3220 7
+#define GSM48_MM_EVENT_TIMEOUT_T3230 8
+#define GSM48_MM_EVENT_TIMEOUT_T3240 9
+#define GSM48_MM_EVENT_IMSI_DETACH 10
+#define GSM48_MM_EVENT_POWER_OFF 11
+#define GSM48_MM_EVENT_PAGING 12
+#define GSM48_MM_EVENT_AUTH_RESPONSE 13
+#define GSM48_MM_EVENT_SYSINFO 14
+#define GSM48_MM_EVENT_USER_PLMN_SEL 15
+
+/* message for MM events */
+struct gsm48_mm_event {
+ uint32_t msg_type;
+
+ uint8_t sres[4];
+};
+
+/* GSM 04.08 MM timers */
+#define GSM_T3210_MS 20, 0
+#define GSM_T3211_MS 15, 0
+/* T3212 is given by SYSTEM INFORMATION */
+#define GSM_T3213_MS 4, 0
+#define GSM_T3220_MS 5, 0
+#define GSM_T3230_MS 15, 0
+#define GSM_T3240_MS 10, 0
+#define GSM_T3241_MS 300, 0
+
+/* MM sublayer instance */
+struct gsm48_mmlayer {
+ struct osmocom_ms *ms;
+ int state;
+ int substate;
+
+ /* queue for RR-SAP, MMxx-SAP, MMR-SAP, events message upwards */
+ struct llist_head rr_upqueue;
+ struct llist_head mmxx_upqueue;
+ struct llist_head mmr_downqueue;
+ struct llist_head event_queue;
+
+ /* timers */
+ struct timer_list t3210, t3211, t3212, t3213;
+ struct timer_list t3220, t3230, t3240;
+ int t3212_value;
+ int start_t3211; /* remember to start timer */
+
+ /* list of MM connections */
+ struct llist_head mm_conn;
+
+ /* network name */
+ char name_short[32];
+ char name_long[32];
+
+ /* location update */
+ uint8_t lupd_pending; /* current pending loc. upd. */
+ uint8_t lupd_type; /* current coded type */
+ uint8_t lupd_attempt; /* attempt counter */
+ uint8_t lupd_ra_failure;/* random access failed */
+ uint8_t lupd_rej_cause; /* cause of last reject */
+ uint8_t lupd_periodic; /* periodic update pending */
+ uint8_t lupd_retry; /* pending T3211/T3213 to */
+ uint16_t lupd_mcc, lupd_mnc, lupd_lac;
+
+ /* imsi detach */
+ uint8_t delay_detach; /* do detach when possible */
+
+ /* other */
+ int mr_substate; /* rem most recent substate */
+ uint8_t power_off; /* set, if power off after detach */
+};
+
+/* MM connection entry */
+struct gsm48_mm_conn {
+ struct llist_head list;
+ struct gsm48_mmlayer *mm;
+
+ /* ref and type form a unique tupple */
+ uint32_t ref; /* reference to trans */
+ uint8_t protocol;
+ uint8_t transaction_id;
+
+ int state;
+};
+
+int gsm48_mm_init(struct osmocom_ms *ms);
+int gsm48_mm_exit(struct osmocom_ms *ms);
+struct msgb *gsm48_mmr_msgb_alloc(int msg_type);
+struct msgb *gsm48_mmevent_msgb_alloc(int msg_type);
+int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg);
+int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg);
+int gsm48_rr_dequeue(struct osmocom_ms *ms);
+int gsm48_mmxx_dequeue(struct osmocom_ms *ms);
+int gsm48_mmr_dequeue(struct osmocom_ms *ms);
+int gsm48_mmevent_dequeue(struct osmocom_ms *ms);
+int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg);
+struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref,
+ uint8_t transaction_id);
+const char *get_mmr_name(int value);
+const char *get_mmxx_name(int value);
+extern const char *gsm48_mm_state_names[];
+extern const char *gsm48_mm_substate_names[];
+
+#endif /* _GSM48_MM_H */
diff --git a/src/host/layer23/include/osmocom/gsm48_rr.h b/src/host/layer23/include/osmocom/gsm48_rr.h
new file mode 100644
index 00000000..74ccb27a
--- /dev/null
+++ b/src/host/layer23/include/osmocom/gsm48_rr.h
@@ -0,0 +1,167 @@
+#ifndef _GSM48_RR_H
+#define _GSM48_RR_H
+
+#include "osmocore/protocol/gsm_04_08.h"
+
+#define GSM_TA_CM 55385
+
+/* GSM 04.07 9.1.2 */
+#define GSM48_RR_EST_REQ 0x10
+#define GSM48_RR_EST_IND 0x12
+#define GSM48_RR_EST_CNF 0x11
+#define GSM48_RR_REL_IND 0x22
+#define GSM48_RR_SYNC_IND 0x32
+#define GSM48_RR_DATA_REQ 0x40
+#define GSM48_RR_DATA_IND 0x42
+#define GSM48_RR_UNIT_DATA_IND 0x52
+#define GSM48_RR_ABORT_REQ 0x60
+#define GSM48_RR_ABORT_IND 0x62
+#define GSM48_RR_ACT_REQ 0x70
+
+#define RR_EST_CAUSE_EMERGENCY 1
+#define RR_EST_CAUSE_REESTAB_TCH_F 2
+#define RR_EST_CAUSE_REESTAB_TCH_H 3
+#define RR_EST_CAUSE_REESTAB_2_TCH_H 4
+#define RR_EST_CAUSE_ANS_PAG_ANY 5
+#define RR_EST_CAUSE_ANS_PAG_SDCCH 6
+#define RR_EST_CAUSE_ANS_PAG_TCH_F 7
+#define RR_EST_CAUSE_ANS_PAG_TCH_ANY 8
+#define RR_EST_CAUSE_ORIG_TCHF 9
+#define RR_EST_CAUSE_LOC_UPD 12
+#define RR_EST_CAUSE_OTHER_SDCCH 13
+
+#define RR_REL_CAUSE_UNDEFINED 0
+#define RR_REL_CAUSE_NORMAL 1
+#define RR_REL_CAUSE_NOT_AUTHORIZED 2
+#define RR_REL_CAUSE_RA_FAILURE 3
+#define RR_REL_CAUSE_T3122 4
+#define RR_REL_CAUSE_TRY_LATER 5
+#define RR_REL_CAUSE_EMERGENCY_ONLY 6
+#define RR_REL_CAUSE_LOST_SIGNAL 7
+
+#define L3_ALLOC_SIZE 256
+#define L3_ALLOC_HEADROOM 64
+
+#define RR_ALLOC_SIZE 256
+#define RR_ALLOC_HEADROOM 64
+
+/* GSM 04.08 RR-SAP header */
+struct gsm48_rr_hdr {
+ uint32_t msg_type; /* RR-* primitive */
+ uint8_t cause;
+};
+
+/* GSM 04.07 9.1.1 */
+#define GSM48_RR_ST_IDLE 0
+#define GSM48_RR_ST_CONN_PEND 1
+#define GSM48_RR_ST_DEDICATED 2
+#define GSM48_RR_ST_REL_PEND 3
+
+/* channel description */
+struct gsm48_rr_cd {
+ uint8_t tsc;
+ uint8_t h; /* using hopping */
+ uint16_t arfcn; /* dedicated mode */
+ uint8_t maio;
+ uint8_t hsn;
+ uint8_t chan_nr; /* type, slot, sub slot */
+ uint8_t link_id;
+ uint8_t ta; /* timing advance */
+ uint8_t mob_alloc_lv[9]; /* len + up to 64 bits */
+ uint8_t start_t1, start_t2, start_t3; /* start. time */
+};
+
+/* measurements */
+struct gsm48_rr_meas {
+ uint8_t rxlev_full;
+ uint8_t rxlev_sub;
+ uint8_t rxqual_full;
+ uint8_t rxqual_sub;
+ uint8_t dtx;
+ uint8_t ba;
+ uint8_t meas_valid;
+ uint8_t ncell_na;
+ uint8_t count;
+ uint8_t rxlev_nc[6];
+ uint8_t bsic_nc[6];
+ uint8_t bcch_f_nc[6];
+};
+
+struct gsm48_cr_hist {
+ uint32_t fn;
+ uint8_t chan_req;
+ uint8_t valid;
+};
+
+/* RR sublayer instance */
+struct gsm48_rrlayer {
+ struct osmocom_ms *ms;
+ int state;
+
+ /* queue for RSL-SAP message upwards */
+ struct llist_head rsl_upqueue;
+
+ /* queue for messages while RR connection is built up */
+ struct llist_head downqueue;
+
+ /* timers */
+ struct timer_list t_rel_wait; /* wait for L2 to transmit UA */
+ struct timer_list t3110;
+ struct timer_list t3122;
+ struct timer_list t3124;
+ struct timer_list t3126;
+ int t3126_value;
+#ifndef TODO
+ struct timer_list temp_rach_ti; /* temporary timer */
+#endif
+
+ /* states if RR-EST-REQ was used */
+ uint8_t rr_est_req;
+ struct msgb *rr_est_msg;
+
+ /* channel request states */
+ uint8_t wait_assign; /* waiting for assignment state */
+ uint8_t n_chan_req; /* number left, incl. current */
+ uint8_t chan_req_val; /* current request value */
+ uint8_t chan_req_mask; /* mask of random bits */
+
+ /* cr_hist must be signed and greater 8 bit, -1 = no value */
+ struct gsm48_cr_hist cr_hist[3];
+
+ /* current channel descriptions */
+ struct gsm48_rr_cd cd_now;
+
+ /* current cipering */
+ uint8_t cipher_on;
+ uint8_t cipher_type; /* 10.5.2.9 */
+
+ /* special states when changing channel */
+ uint8_t hando_susp_state;
+ uint8_t assign_susp_state;
+ uint8_t resume_last_state;
+ struct gsm48_rr_cd cd_last;
+
+ /* measurements */
+ struct gsm48_rr_meas meas;
+
+ /* BA range */
+ uint8_t ba_ranges;
+ uint32_t ba_range[16];
+};
+
+const char *get_rr_name(int value);
+extern int gsm48_rr_init(struct osmocom_ms *ms);
+extern int gsm48_rr_exit(struct osmocom_ms *ms);
+int gsm48_rsl_dequeue(struct osmocom_ms *ms);
+int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg);
+struct msgb *gsm48_l3_msgb_alloc(void);
+struct msgb *gsm48_rr_msgb_alloc(int msg_type);
+int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
+ uint16_t *mnc, uint16_t *lac);
+int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm);
+int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg);
+int gsm48_rr_los(struct osmocom_ms *ms);
+int gsm48_rr_rach_conf(struct osmocom_ms *ms, uint32_t fn);
+extern const char *gsm48_rr_state_names[];
+
+#endif /* _GSM48_RR_H */
diff --git a/src/host/layer23/include/osmocom/gsmtap_util.h b/src/host/layer23/include/osmocom/gsmtap_util.h
new file mode 100644
index 00000000..30fc1267
--- /dev/null
+++ b/src/host/layer23/include/osmocom/gsmtap_util.h
@@ -0,0 +1,16 @@
+#ifndef _GSMTAP_UTIL_H
+#define _GSMTAP_UTIL_H
+
+#include <stdint.h>
+
+/* convert RSL channel number to GSMTAP channel type */
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
+ uint32_t fn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, unsigned int len);
+
+int gsmtap_init(uint32_t dst_ip);
+
+#endif /* _GSMTAP_UTIL_H */
diff --git a/src/host/layer23/include/osmocom/l1ctl.h b/src/host/layer23/include/osmocom/l1ctl.h
new file mode 100644
index 00000000..e5b91e7c
--- /dev/null
+++ b/src/host/layer23/include/osmocom/l1ctl.h
@@ -0,0 +1,45 @@
+#ifndef osmocom_l1ctl_h
+#define osmocom_l1ctl_h
+
+#include <osmocore/msgb.h>
+#include <osmocom/osmocom_data.h>
+
+struct osmocom_ms;
+
+/* Receive incoming data from L1 using L1CTL format */
+int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg);
+
+/* Transmit L1CTL_DATA_REQ */
+int tx_ph_data_req(struct osmocom_ms *ms, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id);
+
+/* Transmit L1CTL_RACH_REQ */
+int tx_ph_rach_req(struct osmocom_ms *ms);
+
+/* Transmit L1CTL_DM_EST_REQ */
+int tx_ph_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn,
+ uint8_t chan_nr, uint8_t tsc, uint8_t tx_power);
+int tx_ph_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn,
+ uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc,
+ uint8_t tx_power);
+
+/* Transmit L1CTL_DM_REL_REQ */
+int tx_ph_dm_rel_req(struct osmocom_ms *ms);
+
+/* Transmit FBSB_REQ */
+int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn,
+ uint8_t flags, uint16_t timeout, uint8_t sync_info_idx,
+ uint8_t ccch_mode);
+
+int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode);
+
+int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len);
+
+/* Transmit L1CTL_RESET_REQ */
+int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type);
+
+/* Transmit L1CTL_PM_REQ */
+int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from,
+ uint16_t arfcm_to);
+
+#endif
diff --git a/src/host/layer23/include/osmocom/l1l2_interface.h b/src/host/layer23/include/osmocom/l1l2_interface.h
new file mode 100644
index 00000000..41403d87
--- /dev/null
+++ b/src/host/layer23/include/osmocom/l1l2_interface.h
@@ -0,0 +1,8 @@
+#ifndef _L1L2_INTERFACE_H
+#define _L1L2_INTERFACE_H
+
+int layer2_open(struct osmocom_ms *ms, const char *socket_path);
+int layer2_close(struct osmocom_ms *ms);
+int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg);
+
+#endif /* _L1L2_INTERFACE_H */
diff --git a/src/host/layer23/include/osmocom/l23_app.h b/src/host/layer23/include/osmocom/l23_app.h
new file mode 100644
index 00000000..1a228567
--- /dev/null
+++ b/src/host/layer23/include/osmocom/l23_app.h
@@ -0,0 +1,10 @@
+#ifndef _L23_APP_H
+#define _L23_APP_H
+
+/* initialization, called once when starting the app, before entering
+ * select loop */
+extern int l23_app_init(struct osmocom_ms *ms);
+extern int (*l23_app_work) (struct osmocom_ms *ms);
+extern int (*l23_app_exit) (struct osmocom_ms *ms);
+
+#endif /* _L23_APP_H */
diff --git a/src/host/layer23/include/osmocom/lapdm.h b/src/host/layer23/include/osmocom/lapdm.h
new file mode 100644
index 00000000..06ac5f89
--- /dev/null
+++ b/src/host/layer23/include/osmocom/lapdm.h
@@ -0,0 +1,94 @@
+#ifndef _OSMOCOM_LAPDM_H
+#define _OSMOCOM_LAPDM_H
+
+#include <stdint.h>
+
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+
+#include <l1a_l23_interface.h>
+
+enum lapdm_state {
+ LAPDm_STATE_NULL = 0,
+ LAPDm_STATE_IDLE,
+ LAPDm_STATE_SABM_SENT,
+ LAPDm_STATE_MF_EST,
+ LAPDm_STATE_TIMER_RECOV,
+ LAPDm_STATE_DISC_SENT,
+};
+
+struct lapdm_entity;
+struct osmocom_ms;
+
+struct lapdm_msg_ctx {
+ struct lapdm_datalink *dl;
+ int lapdm_fmt;
+ uint8_t n201;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint8_t addr;
+ uint8_t ctrl;
+};
+
+/* TS 04.06 / Section 3.5.2 */
+struct lapdm_datalink {
+ uint8_t V_send; /* seq nr of next I frame to be transmitted */
+ uint8_t V_ack; /* last frame ACKed by peer */
+ uint8_t N_send; /* ? set to V_send at Tx time*/
+ uint8_t V_recv; /* seq nr of next I frame expected to be received */
+ uint8_t N_recv; /* expected send seq nr of the next received I frame */
+ uint32_t state;
+ int seq_err_cond; /* condition of sequence error */
+ uint8_t own_busy, peer_busy;
+ struct timer_list t200;
+ uint8_t retrans_ctr;
+ struct llist_head send_queue; /* frames from L3 */
+ struct msgb *send_buffer; /* current frame transmitting */
+ int send_out; /* how much was sent from send_buffer */
+ uint8_t tx_hist[8][200]; /* tx history buffer */
+ int tx_length[8]; /* length in history buffer */
+ struct llist_head tx_queue; /* frames to L1 */
+ struct lapdm_msg_ctx mctx; /* context of established connection */
+ struct msgb *rcv_buffer; /* buffer to assemble the received message */
+
+ struct lapdm_entity *entity;
+};
+
+enum lapdm_dl_sapi {
+ DL_SAPI0 = 0,
+ DL_SAPI3 = 1,
+ _NR_DL_SAPI
+};
+
+struct lapdm_entity {
+ struct lapdm_datalink datalink[_NR_DL_SAPI];
+ int last_tx_dequeue; /* last entity that was dequeued */
+ int tx_pending; /* currently a pending frame not confirmed by L1 */
+ struct osmocom_ms *ms;
+};
+
+const char *get_rsl_name(int value);
+extern const char *lapdm_state_names[];
+
+/* initialize a LAPDm entity */
+void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms);
+
+/* deinitialize a LAPDm entity */
+void lapdm_exit(struct lapdm_entity *le);
+
+/* input into layer2 (from layer 1) */
+int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i);
+int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le);
+
+/* input into layer2 (from layer 3) */
+int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms);
+
+/* sending messages up from L2 to L3 */
+int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms);
+
+typedef int (*osmol2_cb_t)(struct msgb *msg, struct osmocom_ms *ms);
+
+/* register message handler for messages that are sent from L2->L3 */
+int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb);
+
+#endif /* _OSMOCOM_LAPDM_H */
diff --git a/src/host/layer23/include/osmocom/layer3.h b/src/host/layer23/include/osmocom/layer3.h
new file mode 100644
index 00000000..bf68102d
--- /dev/null
+++ b/src/host/layer23/include/osmocom/layer3.h
@@ -0,0 +1,14 @@
+#ifndef _OSMOCOM_L3_H
+#define _OSMOCOM_L3_H
+
+#include <osmocore/msgb.h>
+#include <osmocom/osmocom_data.h>
+
+int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms);
+int gsm48_rx_dcch(struct msgb *msg, struct osmocom_ms *ms);
+int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms);
+
+/* Initialize layer3 for the MS, hook it to L2 */
+int layer3_init(struct osmocom_ms *ms);
+
+#endif
diff --git a/src/host/layer23/include/osmocom/logging.h b/src/host/layer23/include/osmocom/logging.h
new file mode 100644
index 00000000..1a11cf9d
--- /dev/null
+++ b/src/host/layer23/include/osmocom/logging.h
@@ -0,0 +1,25 @@
+#ifndef _LOGGING_H
+#define _LOGGING_H
+
+#define DEBUG
+#include <osmocore/logging.h>
+
+enum {
+ DRSL,
+ DRR,
+ DPLMN,
+ DCS,
+ DMM,
+ DCC,
+ DSMS,
+ DMNCC,
+ DMEAS,
+ DPAG,
+ DLAPDM,
+ DL1C,
+ DSUM,
+};
+
+extern const struct log_info log_info;
+
+#endif /* _LOGGING_H */
diff --git a/src/host/layer23/include/osmocom/mncc.h b/src/host/layer23/include/osmocom/mncc.h
new file mode 100644
index 00000000..d1d4d38f
--- /dev/null
+++ b/src/host/layer23/include/osmocom/mncc.h
@@ -0,0 +1,166 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 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.
+ *
+ */
+
+#ifndef _MNCC_H
+#define _MNCC_H
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/mncc.h>
+
+struct gsm_call {
+ struct llist_head entry;
+
+ void *ms;
+
+ uint32_t callref;
+
+ uint8_t hold; /* call on hold */
+ uint8_t ring; /* call is ringing/knocking */
+};
+
+#define MNCC_SETUP_REQ 0x0101
+#define MNCC_SETUP_IND 0x0102
+#define MNCC_SETUP_RSP 0x0103
+#define MNCC_SETUP_CNF 0x0104
+#define MNCC_SETUP_COMPL_REQ 0x0105
+#define MNCC_SETUP_COMPL_IND 0x0106
+/* MNCC_REJ_* is perfomed via MNCC_REL_* */
+#define MNCC_CALL_CONF_IND 0x0107
+#define MNCC_CALL_PROC_REQ 0x0108
+#define MNCC_PROGRESS_REQ 0x0109
+#define MNCC_ALERT_REQ 0x010a
+#define MNCC_ALERT_IND 0x010b
+#define MNCC_NOTIFY_REQ 0x010c
+#define MNCC_NOTIFY_IND 0x010d
+#define MNCC_DISC_REQ 0x010e
+#define MNCC_DISC_IND 0x010f
+#define MNCC_REL_REQ 0x0110
+#define MNCC_REL_IND 0x0111
+#define MNCC_REL_CNF 0x0112
+#define MNCC_FACILITY_REQ 0x0113
+#define MNCC_FACILITY_IND 0x0114
+#define MNCC_START_DTMF_IND 0x0115
+#define MNCC_START_DTMF_RSP 0x0116
+#define MNCC_START_DTMF_REJ 0x0117
+#define MNCC_STOP_DTMF_IND 0x0118
+#define MNCC_STOP_DTMF_RSP 0x0119
+#define MNCC_MODIFY_REQ 0x011a
+#define MNCC_MODIFY_IND 0x011b
+#define MNCC_MODIFY_RSP 0x011c
+#define MNCC_MODIFY_CNF 0x011d
+#define MNCC_MODIFY_REJ 0x011e
+#define MNCC_HOLD_IND 0x011f
+#define MNCC_HOLD_CNF 0x0120
+#define MNCC_HOLD_REJ 0x0121
+#define MNCC_RETRIEVE_IND 0x0122
+#define MNCC_RETRIEVE_CNF 0x0123
+#define MNCC_RETRIEVE_REJ 0x0124
+#define MNCC_USERINFO_REQ 0x0125
+#define MNCC_USERINFO_IND 0x0126
+#define MNCC_REJ_REQ 0x0127
+#define MNCC_REJ_IND 0x0128
+#define MNCC_PROGRESS_IND 0x0129
+#define MNCC_CALL_PROC_IND 0x012a
+#define MNCC_CALL_CONF_REQ 0x012b
+#define MNCC_START_DTMF_REQ 0x012c
+#define MNCC_STOP_DTMF_REQ 0x012d
+#define MNCC_HOLD_REQ 0x012e
+#define MNCC_RETRIEVE_REQ 0x012f
+
+#define MNCC_BRIDGE 0x0200
+#define MNCC_FRAME_RECV 0x0201
+#define MNCC_FRAME_DROP 0x0202
+#define MNCC_LCHAN_MODIFY 0x0203
+
+#define GSM_TCHF_FRAME 0x0300
+#define GSM_TCHF_FRAME_EFR 0x0301
+
+#define GSM_MAX_FACILITY 128
+#define GSM_MAX_SSVERSION 128
+#define GSM_MAX_USERUSER 128
+
+#define MNCC_F_BEARER_CAP 0x0001
+#define MNCC_F_CALLED 0x0002
+#define MNCC_F_CALLING 0x0004
+#define MNCC_F_REDIRECTING 0x0008
+#define MNCC_F_CONNECTED 0x0010
+#define MNCC_F_CAUSE 0x0020
+#define MNCC_F_USERUSER 0x0040
+#define MNCC_F_PROGRESS 0x0080
+#define MNCC_F_EMERGENCY 0x0100
+#define MNCC_F_FACILITY 0x0200
+#define MNCC_F_SSVERSION 0x0400
+#define MNCC_F_CCCAP 0x0800
+#define MNCC_F_KEYPAD 0x1000
+#define MNCC_F_SIGNAL 0x2000
+
+struct gsm_mncc {
+ /* context based information */
+ u_int32_t msg_type;
+ u_int32_t callref;
+
+ /* which fields are present */
+ u_int32_t fields;
+
+ /* data derived informations (MNCC_F_ based) */
+ struct gsm_mncc_bearer_cap bearer_cap;
+ struct gsm_mncc_number called;
+ struct gsm_mncc_number calling;
+ struct gsm_mncc_number redirecting;
+ struct gsm_mncc_number connected;
+ struct gsm_mncc_cause cause;
+ struct gsm_mncc_progress progress;
+ struct gsm_mncc_useruser useruser;
+ struct gsm_mncc_facility facility;
+ struct gsm_mncc_cccap cccap;
+ struct gsm_mncc_ssversion ssversion;
+ struct {
+ int sup;
+ int inv;
+ } clir;
+ int signal;
+
+ /* data derived information, not MNCC_F based */
+ int keypad;
+ int more;
+ int notify; /* 0..127 */
+ int emergency;
+ char imsi[16];
+
+ unsigned char lchan_mode;
+};
+
+struct gsm_data_frame {
+ u_int32_t msg_type;
+ u_int32_t callref;
+ unsigned char data[0];
+};
+
+const char *get_mncc_name(int value);
+int mncc_recv(struct osmocom_ms *ms, int msg_type, void *arg);
+void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
+
+#endif
+
diff --git a/src/host/layer23/include/osmocom/networks.h b/src/host/layer23/include/osmocom/networks.h
new file mode 100644
index 00000000..e8c1b18e
--- /dev/null
+++ b/src/host/layer23/include/osmocom/networks.h
@@ -0,0 +1,22 @@
+#ifndef _NETWORKS_H
+#define _NETWORKS_H
+
+struct gsm_networks {
+ uint16_t mcc;
+ int16_t mnc;
+ const char *name;
+};
+
+int gsm_match_mcc(uint16_t mcc, char *imsi);
+int gsm_match_mnc(uint16_t mcc, uint8_t mnc, char *imsi);
+const char *gsm_print_mcc(uint16_t mcc);
+const char *gsm_print_mnc(uint16_t mcc);
+const char *gsm_get_mcc(uint16_t mcc);
+const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc);
+const char *gsm_imsi_mcc(char *imsi);
+const char *gsm_imsi_mnc(char *imsi);
+const uint16_t gsm_input_mcc(char *string);
+const uint16_t gsm_input_mnc(char *string);
+
+#endif /* _NETWORKS_H */
+
diff --git a/src/host/layer23/include/osmocom/osmocom_data.h b/src/host/layer23/include/osmocom/osmocom_data.h
new file mode 100644
index 00000000..e4430fc7
--- /dev/null
+++ b/src/host/layer23/include/osmocom/osmocom_data.h
@@ -0,0 +1,80 @@
+#ifndef osmocom_data_h
+#define osmocom_data_h
+
+#include <osmocore/select.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/write_queue.h>
+
+struct osmocom_ms;
+
+#include <osmocom/support.h>
+#include <osmocom/settings.h>
+#include <osmocom/subscriber.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/gsm48_rr.h>
+#include <osmocom/sysinfo.h>
+#include <osmocom/gsm322.h>
+#include <osmocom/gsm48_mm.h>
+#include <osmocom/gsm48_cc.h>
+
+/* A layer2 entity */
+struct osmol2_entity {
+ struct lapdm_entity lapdm_dcch;
+ struct lapdm_entity lapdm_acch;
+ osmol2_cb_t msg_handler;
+};
+
+/* One Mobilestation for osmocom */
+struct osmocom_ms {
+ struct llist_head entity;
+ char name[32];
+ struct write_queue wq;
+ uint16_t test_arfcn;
+
+ struct gsm_support support;
+
+ struct gsm_settings settings;
+
+ struct gsm_subscriber subscr;
+
+ struct osmol2_entity l2_entity;
+
+ struct gsm48_rrlayer rrlayer;
+ struct gsm322_plmn plmn;
+ struct gsm322_cellsel cellsel;
+ struct gsm48_mmlayer mmlayer;
+ struct gsm48_cclayer cclayer;
+ struct llist_head trans_list;
+};
+
+enum osmobb_sig_subsys {
+ SS_L1CTL,
+};
+
+enum osmobb_meas_sig {
+ S_L1CTL_FBSB_ERR,
+ S_L1CTL_FBSB_RESP,
+ S_L1CTL_RESET,
+ S_L1CTL_PM_RES,
+ S_L1CTL_PM_DONE,
+ S_L1CTL_RACH_CONF,
+ S_L1CTL_CCCH_MODE_CONF,
+};
+
+struct osmobb_meas_res {
+ struct osmocom_ms *ms;
+ uint16_t band_arfcn;
+ uint8_t rx_lev;
+};
+
+struct osmobb_rach_conf {
+ struct osmocom_ms *ms;
+ uint32_t fn;
+};
+
+struct osmobb_ccch_mode_conf {
+ struct osmocom_ms *ms;
+ uint8_t ccch_mode;
+};
+
+#endif
diff --git a/src/host/layer23/include/osmocom/rslms.h b/src/host/layer23/include/osmocom/rslms.h
new file mode 100644
index 00000000..8f17b818
--- /dev/null
+++ b/src/host/layer23/include/osmocom/rslms.h
@@ -0,0 +1,23 @@
+#ifndef _OSMOCOM_RSLMS_H
+#define _OSMOCOM_RSLMS_H
+
+#include <osmocore/msgb.h>
+#include <osmocom/osmocom_data.h>
+
+/* From L3 into RSLMS (direction -> L2) */
+
+/* Send a 'simple' RLL request to L2 */
+int rslms_tx_rll_req(struct osmocom_ms *ms, uint8_t msg_type,
+ uint8_t chan_nr, uint8_t link_id);
+
+/* Send a RLL request (including L3 info) to L2 */
+int rslms_tx_rll_req_l3(struct osmocom_ms *ms, uint8_t msg_type,
+ uint8_t chan_nr, uint8_t link_id, struct msgb *msg);
+
+
+/* From L2 into RSLMS (direction -> L3) */
+
+/* input function that L2 calls when sending messages up to L3 */
+//int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms);
+
+#endif /* _OSMOCOM_RSLMS_H */
diff --git a/src/host/layer23/include/osmocom/settings.h b/src/host/layer23/include/osmocom/settings.h
new file mode 100644
index 00000000..95c53a83
--- /dev/null
+++ b/src/host/layer23/include/osmocom/settings.h
@@ -0,0 +1,34 @@
+#ifndef _settings_h
+#define _settings_h
+
+struct gsm_settings {
+ /* IMEI */
+ char imei[16];
+ char imeisv[17];
+ char imei_random;
+
+ /* network search */
+ int plmn_mode; /* PLMN_MODE_* */
+
+ /* SIM */
+ int simtype; /* selects card on power on */
+ char emergency_imsi[20]; /* just in case... */
+
+ /* test card simulator settings */
+ char test_imsi[20]; /* just in case... */
+ uint8_t test_barr;
+ uint8_t test_rplmn_valid;
+ uint16_t test_rplmn_mcc, test_rplmn_mnc;
+ uint8_t test_always; /* ...search hplmn... */
+
+ /* call related settings */
+ uint8_t cw; /* set if call-waiting is allowed */
+ uint8_t clip, clir;
+};
+
+int gsm_settings_init(struct osmocom_ms *ms);
+char *gsm_check_imei(const char *imei, const char *sv);
+int gsm_random_imei(struct gsm_settings *set);
+
+#endif /* _settings_h */
+
diff --git a/src/host/layer23/include/osmocom/subscriber.h b/src/host/layer23/include/osmocom/subscriber.h
new file mode 100644
index 00000000..09eab23c
--- /dev/null
+++ b/src/host/layer23/include/osmocom/subscriber.h
@@ -0,0 +1,89 @@
+#ifndef _SUBSCRIBER_H
+#define _SUBSCRIBER_H
+
+/* GSM 04.08 4.1.2.2 SIM update status */
+#define GSM_SIM_U0_NULL 0
+#define GSM_SIM_U1_UPDATED 1
+#define GSM_SIM_U2_NOT_UPDATED 2
+#define GSM_SIM_U3_ROAMING_NA 3
+
+struct gsm_sub_plmn_list {
+ struct llist_head entry;
+ uint16_t mcc, mnc;
+};
+
+struct gsm_sub_plmn_na {
+ struct llist_head entry;
+ uint16_t mcc, mnc;
+ uint8_t cause;
+};
+
+#define GSM_IMSI_LENGTH 16
+
+enum {
+ GSM_SIM_TYPE_NONE = 0,
+ GSM_SIM_TYPE_SLOT,
+ GSM_SIM_TYPE_TEST
+};
+
+struct gsm_subscriber {
+ struct osmocom_ms *ms;
+
+ /* status */
+ uint8_t sim_valid; /* sim inserted and valid */
+ uint8_t ustate; /* update status */
+ uint8_t imsi_attached; /* attached state */
+
+ /* LAI */
+ uint8_t lai_valid;
+ uint16_t lai_mcc, lai_mnc, lai_lac;
+
+ /* IMSI */
+ char imsi[GSM_IMSI_LENGTH];
+
+ /* TMSI */
+ uint8_t tmsi_valid;
+ uint32_t tmsi;
+
+ /* key */
+ uint8_t key_seq; /* ciphering key sequence number */
+ uint8_t key[32]; /* up to 256 bit */
+
+ /* other */
+ struct llist_head plmn_list; /* PLMN Selector field */
+ struct llist_head plmn_na; /* not allowed PLMNs */
+ uint8_t t6m_hplmn; /* timer for hplmn search */
+
+ /* special things */
+ uint8_t always_search_hplmn;
+ /* search hplmn in other countries also (for test cards) */
+ char sim_name[32]; /* name to load/save sim */
+
+ /* PLMN last registered */
+ uint8_t plmn_valid;
+ uint16_t plmn_mcc, plmn_mnc;
+
+ /* our access */
+ uint8_t acc_barr; /* if we may access, if cell barred */
+ uint16_t acc_class; /* bitmask of what we may access */
+};
+
+int gsm_subscr_init(struct osmocom_ms *ms);
+int gsm_subscr_exit(struct osmocom_ms *ms);
+int gsm_subscr_testcard(struct osmocom_ms *ms);
+int gsm_subscr_remove(struct osmocom_ms *ms);
+void new_sim_ustate(struct gsm_subscriber *subscr, int state);
+int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
+ uint16_t mnc);
+int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
+ uint16_t mnc, uint8_t cause);
+int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
+ uint16_t mnc);
+int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms,
+ void (*print)(void *, const char *, ...), void *priv);
+void gsm_subscr_dump(struct gsm_subscriber *subscr,
+ void (*print)(void *, const char *, ...), void *priv);
+char *gsm_check_imsi(const char *imsi);
+
+#endif /* _SUBSCRIBER_H */
+
diff --git a/src/host/layer23/include/osmocom/support.h b/src/host/layer23/include/osmocom/support.h
new file mode 100644
index 00000000..ad1ccd7e
--- /dev/null
+++ b/src/host/layer23/include/osmocom/support.h
@@ -0,0 +1,93 @@
+#ifndef _SUPPORT_H
+#define _SUPPORT_H
+
+#define GSM_CIPHER_A5_1 0
+#define GSM_CIPHER_A5_2 1
+#define GSM_CIPHER_A5_3 2
+#define GSM_CIPHER_A5_4 3
+#define GSM_CIPHER_A5_5 4
+#define GSM_CIPHER_A5_6 5
+#define GSM_CIPHER_A5_7 6
+#define GSM_CIPHER_RESERVED 7
+
+struct gsm_support {
+ struct osmocom_ms *ms;
+
+ /* rf power capability */
+ uint8_t pwr_lev_900; /* and < 900 */
+ uint8_t pwr_lev_1800; /* DCS and PCS */
+ /* controlled early classmark sending */
+ uint8_t es_ind;
+ /* revision level */
+ uint8_t rev_lev;
+ /* support of VGCS */
+ uint8_t vgcs;
+ /* support of VBS */
+ uint8_t vbs;
+ /* support of SMS */
+ uint8_t sms_ptp;
+ /* screening indicator */
+ uint8_t ss_ind;
+ /* pseudo synchronised capability */
+ uint8_t ps_cap;
+ /* CM service prompt */
+ uint8_t cmsp;
+ /* solsa support */
+ uint8_t solsa;
+ /* location service support */
+ uint8_t lcsva;
+ /* codec supprot */
+ uint8_t a5_1;
+ uint8_t a5_2;
+ uint8_t a5_3;
+ uint8_t a5_4;
+ uint8_t a5_5;
+ uint8_t a5_6;
+ uint8_t a5_7;
+ /* radio support */
+ uint8_t p_gsm;
+ uint8_t e_gsm;
+ uint8_t r_gsm;
+ uint8_t r_capa;
+ uint8_t low_capa;
+ uint8_t dcs_1800;
+ uint8_t dcs_capa;
+ uint8_t freq_map[128];
+ /* multi slot support */
+ uint8_t ms_sup;
+ /* ucs2 treatment */
+ uint8_t ucs2_treat;
+ /* support extended measurements */
+ uint8_t ext_meas;
+ /* support switched measurement capability */
+ uint8_t meas_cap;
+ uint8_t sms_val;
+ uint8_t sm_val;
+ /* positioning method capability */
+ uint8_t loc_serv;
+ uint8_t e_otd_ass;
+ uint8_t e_otd_based;
+ uint8_t gps_ass;
+ uint8_t gps_based;
+ uint8_t gps_conv;
+
+ /* radio */
+ int8_t min_rxlev_db;
+ uint8_t scan_to;
+ uint8_t sync_to;
+};
+
+struct gsm_support_scan_max {
+ uint16_t start;
+ uint16_t end;
+ uint16_t max;
+ uint16_t temp;
+};
+extern struct gsm_support_scan_max gsm_sup_smax[];
+
+void gsm_support_init(struct osmocom_ms *ms);
+void gsm_support_dump(struct gsm_support *sup,
+ void (*print)(void *, const char *, ...), void *priv);
+
+#endif /* _SUPPORT_H */
+
diff --git a/src/host/layer23/include/osmocom/sysinfo.h b/src/host/layer23/include/osmocom/sysinfo.h
new file mode 100644
index 00000000..9a33bb97
--- /dev/null
+++ b/src/host/layer23/include/osmocom/sysinfo.h
@@ -0,0 +1,120 @@
+#ifndef _SYSINFO_H
+#define _SYSINFO_H
+
+/* collection of system information of the current cell */
+
+/* frequency mask flags of frequency type */
+#define FREQ_TYPE_SERV 0x01 /* frequency of the serving cell */
+#define FREQ_TYPE_HOPP 0x02 /* frequency used for channel hopping */
+#define FREQ_TYPE_NCELL 0x1c /* frequency of the neighbor cell */
+#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */
+#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */
+#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */
+#define FREQ_TYPE_REP 0xe0 /* frequency to be reported */
+#define FREQ_TYPE_REP_5 0x20 /* sub channel of SI 5 */
+#define FREQ_TYPE_REP_5bis 0x40 /* sub channel of SI 5bis */
+#define FREQ_TYPE_REP_5ter 0x80 /* sub channel of SI 5ter */
+
+/* structure of one frequency */
+struct gsm_sysinfo_freq {
+ /* if the frequency included in the sysinfo */
+ uint8_t mask;
+};
+
+/* structure of all received system informations */
+struct gsm48_sysinfo {
+ /* flags of available information */
+ uint8_t si1, si2, si2bis, si2ter, si3,
+ si4, si5, si5bis, si5ter, si6;
+
+ /* memory maps to simply detect change in system info messages */
+ uint8_t si1_msg[23];
+ uint8_t si2_msg[23];
+ uint8_t si2b_msg[23];
+ uint8_t si2t_msg[23];
+ uint8_t si3_msg[23];
+ uint8_t si4_msg[23];
+ uint8_t si5_msg[18];
+ uint8_t si5b_msg[18];
+ uint8_t si5t_msg[18];
+ uint8_t si6_msg[18];
+
+ struct gsm_sysinfo_freq freq[1024]; /* all frequencies */
+ uint16_t hopping[64]; /* hopping arfcn */
+ uint8_t hopp_len;
+
+ /* serving cell */
+ uint16_t cell_id;
+ uint16_t mcc, mnc, lac; /* LAI */
+ uint8_t max_retrans; /* decoded */
+ uint8_t tx_integer; /* decoded */
+ uint8_t reest_denied; /* 1 = denied */
+ uint8_t cell_barr; /* 1 = barred */
+ uint16_t class_barr; /* bit 10 is emergency */
+
+ /* si3 rest */
+ uint8_t sp;
+ uint8_t sp_cbq;
+ uint8_t sp_cro;
+ uint8_t sp_to;
+ uint8_t sp_pt;
+ uint8_t po;
+ uint8_t po_value;
+ uint8_t si2ter_ind;
+ uint8_t ecsm;
+ uint8_t sched;
+ uint8_t sched_where;
+ uint8_t gi_ra_colour;
+ uint8_t gi_si13_pos;
+
+ /* cell selection */
+ int8_t ms_txpwr_max_ccch;
+ int8_t cell_resel_hyst_db;
+ int8_t rxlev_acc_min_db;
+ uint8_t neci;
+ uint8_t acs;
+ /* bcch options */
+ uint8_t bcch_radio_link_timeout;
+ uint8_t bcch_dtx;
+ uint8_t bcch_pwrc;
+ /* sacch options */
+ uint8_t sacch_radio_link_timeout;
+ uint8_t sacch_dtx;
+ uint8_t sacch_pwrc;
+ /* control channel */
+ uint8_t ccch_conf;
+ uint8_t bs_ag_blks_res;
+ uint8_t att_allowed;
+ uint8_t pag_mf_periods;
+ int32_t t3212; /* real value in seconds */
+ /* channel description */
+ uint8_t tsc;
+ uint8_t h; /* using hopping */
+ uint16_t arfcn;
+ uint8_t maio;
+ uint8_t hsn;
+ uint8_t chan_nr; /* type, slot, sub slot */
+
+ /* neighbor cell */
+ uint8_t nb_ext_ind_si2;
+ uint8_t nb_ba_ind_si2;
+ uint8_t nb_ext_ind_si2bis;
+ uint8_t nb_ba_ind_si2bis;
+ uint8_t nb_multi_rep_si2ter; /* see GSM 05.08 8.4.3 */
+ uint8_t nb_ext_ind_si5;
+ uint8_t nb_ba_ind_si5;
+ uint8_t nb_ext_ind_si5bis;
+ uint8_t nb_ba_ind_si5bis;
+ uint8_t nb_multi_rep_si5ter;
+ uint8_t nb_ncc_permitted;
+ uint8_t nb_max_retrans; /* decoded */
+ uint8_t nb_tx_integer; /* decoded */
+ uint8_t nb_reest_denied; /* 1 = denied */
+ uint8_t nb_cell_barr; /* 1 = barred */
+ uint16_t nb_class_barr; /* bit 10 is emergency */
+};
+
+int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
+ void (*print)(void *, const char *, ...), void *priv);
+
+#endif /* _SYSINFO_H */
diff --git a/src/host/layer23/include/osmocom/transaction.h b/src/host/layer23/include/osmocom/transaction.h
new file mode 100644
index 00000000..4be82c19
--- /dev/null
+++ b/src/host/layer23/include/osmocom/transaction.h
@@ -0,0 +1,71 @@
+#ifndef _TRANSACT_H
+#define _TRANSACT_H
+
+#include <osmocore/linuxlist.h>
+
+/* One transaction */
+struct gsm_trans {
+ /* Entry in list of all transactions */
+ struct llist_head entry;
+
+ /* The protocol within which we live */
+ uint8_t protocol;
+
+ /* The current transaction ID */
+ uint8_t transaction_id;
+
+ /* To whom we belong */
+ struct osmocom_ms *ms;
+
+ /* reference from MNCC or other application */
+ uint32_t callref;
+
+ /* if traffic channel receive was requested */
+ int tch_recv;
+
+ union {
+ struct {
+
+ /* current call state */
+ int state;
+
+ /* most recent progress indicator */
+ uint8_t prog_ind;
+
+ /* current timer and message queue */
+ int Tcurrent; /* current CC timer */
+ int T308_second; /* used to send release again */
+ struct timer_list timer;
+ struct gsm_mncc msg; /* stores setup/disconnect/release message */
+ } cc;
+#if 0
+ struct {
+ uint8_t link_id; /* RSL Link ID to be used for this trans */
+ int is_mt; /* is this a MO (0) or MT (1) transfer */
+ enum gsm411_cp_state cp_state;
+ struct timer_list cp_timer;
+
+ enum gsm411_rp_state rp_state;
+
+ struct gsm_sms *sms;
+ } sms;
+#endif
+ };
+};
+
+
+
+struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms,
+ uint8_t proto, uint8_t trans_id);
+struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms,
+ uint32_t callref);
+
+struct gsm_trans *trans_alloc(struct osmocom_ms *ms,
+ uint8_t protocol, uint8_t trans_id,
+ uint32_t callref);
+void trans_free(struct gsm_trans *trans);
+
+int trans_assign_trans_id(struct osmocom_ms *ms,
+ uint8_t protocol, uint8_t ti_flag);
+
+#endif
diff --git a/src/host/layer23/include/osmocom/vty.h b/src/host/layer23/include/osmocom/vty.h
new file mode 100644
index 00000000..f9e65a12
--- /dev/null
+++ b/src/host/layer23/include/osmocom/vty.h
@@ -0,0 +1,19 @@
+#ifndef OSMOCOM_VTY_H
+#define OSMOCOM_VTY_H
+
+#include <osmocom/osmocom_data.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/command.h>
+
+enum ms_vty_node {
+ MS_NODE = _LAST_OSMOVTY_NODE + 1,
+ TESTSIM_NODE,
+};
+
+enum node_type ms_vty_go_parent(struct vty *vty);
+int ms_vty_init(void);
+extern void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+#endif
+
diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am
new file mode 100644
index 00000000..6b25352c
--- /dev/null
+++ b/src/host/layer23/src/Makefile.am
@@ -0,0 +1,26 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+#AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
+
+noinst_LIBRARIES = liblayer23.a
+liblayer23_a_SOURCES = l1l2_interface.c l1ctl.c gsmtap_util.c lapdm.c rslms.c \
+ layer3.c logging.c bcch_scan.c settings.c \
+ gsm48_cc.c transaction.c gsm48_mm.c gsm48_rr.c \
+ gsm322.c support.c subscriber.c sysinfo.c networks.c \
+ mnccms.c vty_interface.c
+
+bin_PROGRAMS = bcch_scan layer23 echo_test mobile
+
+bcch_scan_SOURCES = main.c app_bcch_scan.c
+bcch_scan_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS)
+
+layer23_SOURCES = main.c app_phone.c
+layer23_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS)
+
+echo_test_SOURCES = main.c app_echo_test.c
+echo_test_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS)
+
+mobile_SOURCES = main.c app_mobile.c
+mobile_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
+
+
diff --git a/src/host/layer23/src/app_bcch_scan.c b/src/host/layer23/src/app_bcch_scan.c
new file mode 100644
index 00000000..ce5ef9e9
--- /dev/null
+++ b/src/host/layer23/src/app_bcch_scan.c
@@ -0,0 +1,57 @@
+/* "Application" code of the layer2/3 stack */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (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 <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/layer3.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/gsmtap_util.h>
+#include <osmocom/logging.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/signal.h>
+
+static int signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_RESET:
+ ms = signal_data;
+ return fps_start(ms);
+ }
+ return 0;
+}
+
+int l23_app_init(struct osmocom_ms *ms)
+{
+ /* don't do layer3_init() as we don't want an actualy L3 */
+ fps_init(ms);
+ return register_signal_handler(SS_L1CTL, &signal_cb, NULL);
+}
diff --git a/src/host/layer23/src/app_echo_test.c b/src/host/layer23/src/app_echo_test.c
new file mode 100644
index 00000000..285b80ab
--- /dev/null
+++ b/src/host/layer23/src/app_echo_test.c
@@ -0,0 +1,56 @@
+/* TEST code, regularly transmit ECHO REQ packet to L1 */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (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 <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/layer3.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/gsmtap_util.h>
+#include <osmocom/logging.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+
+
+static struct {
+ struct timer_list timer;
+} test_data;
+
+static void test_tmr_cb(void *data)
+{
+ struct osmocom_ms *ms = data;
+
+ l1ctl_tx_echo_req(ms, 62);
+ bsc_schedule_timer(&test_data.timer, 1, 0);
+}
+
+int l23_app_init(struct osmocom_ms *ms)
+{
+ test_data.timer.cb = &test_tmr_cb;
+ test_data.timer.data = ms;
+
+ bsc_schedule_timer(&test_data.timer, 1, 0);
+
+ return 0;
+}
diff --git a/src/host/layer23/src/app_mobile.c b/src/host/layer23/src/app_mobile.c
new file mode 100644
index 00000000..52100e01
--- /dev/null
+++ b/src/host/layer23/src/app_mobile.c
@@ -0,0 +1,191 @@
+/* "Application" code of the layer2/3 stack */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+
+#include <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/l23_app.h>
+#include <osmocom/gsm48_rr.h>
+#include <osmocom/sysinfo.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/gsmtap_util.h>
+#include <osmocom/logging.h>
+#include <osmocom/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/file.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/signal.h>
+
+extern struct log_target *stderr_target;
+static const char *config_file = "/etc/osmocom/osmocom.cfg";
+extern void *l23_ctx;
+extern unsigned short vty_port;
+
+static int started = 0;
+
+int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg);
+
+int mobile_work(struct osmocom_ms *ms)
+{
+ int work = 0, w;
+
+ do {
+ w = 0;
+ w |= gsm48_rsl_dequeue(ms);
+ w |= gsm48_rr_dequeue(ms);
+ w |= gsm48_mmxx_dequeue(ms);
+ w |= gsm48_mmr_dequeue(ms);
+ w |= gsm48_mmevent_dequeue(ms);
+ w |= gsm322_plmn_dequeue(ms);
+ w |= gsm322_cs_dequeue(ms);
+ w |= mncc_dequeue(ms);
+ if (w)
+ work = 1;
+ } while (w);
+ return work;
+}
+
+static int signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+ struct msgb *nmsg;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_RESET:
+ if (started)
+ break;
+ started = 1;
+ ms = signal_data;
+ /* insert test card, if enabled */
+ if (ms->settings.simtype == GSM_SIM_TYPE_TEST)
+ gsm_subscr_testcard(ms);
+ /* start PLMN + cell selection process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+ }
+ return 0;
+}
+
+int mobile_exit(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ if (!mm->power_off && started) {
+ struct msgb *nmsg;
+
+ mm->power_off = 1;
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(mm->ms, nmsg);
+
+ return -EBUSY;
+ }
+
+ /* in case there is a lockup during exit */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+
+ unregister_signal_handler(SS_L1CTL, &signal_cb, NULL);
+ gsm322_exit(ms);
+ gsm48_mm_exit(ms);
+ gsm48_rr_exit(ms);
+ gsm_subscr_exit(ms);
+ gsm48_cc_exit(ms);
+
+ printf("Power off!\n");
+
+ return 0;
+}
+
+static struct vty_app_info vty_info = {
+ .name = "OsmocomBB",
+ .version = PACKAGE_VERSION,
+ .go_parent_cb = ms_vty_go_parent,
+};
+
+int l23_app_init(struct osmocom_ms *ms)
+{
+ int rc;
+ struct telnet_connection dummy_conn;
+
+// log_parse_category_mask(stderr_target, "DRSL:DLAPDM:DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM");
+ log_parse_category_mask(stderr_target, "DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM");
+
+ srand(time(NULL));
+
+ gsm_settings_init(ms);
+ gsm48_cc_init(ms);
+ gsm_support_init(ms);
+ gsm_subscr_init(ms);
+ gsm48_rr_init(ms);
+ gsm48_mm_init(ms);
+ INIT_LLIST_HEAD(&ms->trans_list);
+ ms->cclayer.mncc_recv = mncc_recv_mobile;
+ gsm322_init(ms);
+
+ l23_app_work = mobile_work;
+ register_signal_handler(SS_L1CTL, &signal_cb, NULL);
+ l23_app_exit = mobile_exit;
+
+ vty_init(&vty_info);
+ ms_vty_init();
+ dummy_conn.priv = 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%s'\n", OSMOCOM_CONFDIR, config_file);
+ return rc;
+ }
+ telnet_init(l23_ctx, NULL, vty_port);
+ if (rc < 0)
+ return rc;
+ printf("VTY available on port %u.\n", vty_port);
+
+ gsm_random_imei(&ms->settings);
+
+ printf("Mobile initialized, please start phone now!\n");
+ return 0;
+}
+
diff --git a/src/host/layer23/src/app_phone.c b/src/host/layer23/src/app_phone.c
new file mode 100644
index 00000000..de122f54
--- /dev/null
+++ b/src/host/layer23/src/app_phone.c
@@ -0,0 +1,60 @@
+/* "Application" code of the layer2/3 stack */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (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 <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/layer3.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/gsmtap_util.h>
+#include <osmocom/logging.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/signal.h>
+
+static int signal_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_RESET:
+ ms = signal_data;
+ return l1ctl_tx_fbsb_req(ms, ms->test_arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ CCCH_MODE_NONE);
+ break;
+ }
+ return 0;
+}
+
+
+int l23_app_init(struct osmocom_ms *ms)
+{
+ register_signal_handler(SS_L1CTL, &signal_cb, NULL);
+ return layer3_init(ms);
+}
diff --git a/src/host/layer23/src/bcch_scan.c b/src/host/layer23/src/bcch_scan.c
new file mode 100644
index 00000000..c542129a
--- /dev/null
+++ b/src/host/layer23/src/bcch_scan.c
@@ -0,0 +1,319 @@
+/* BCCH Scanning code for OsmocomBB */
+
+/* (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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <l1a_l23_interface.h>
+
+#include <osmocore/logging.h>
+#include <osmocore/talloc.h>
+#include <osmocore/signal.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/protocol/gsm_08_58.h>
+#include <osmocore/rsl.h>
+
+#include <osmocom/l1ctl.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/logging.h>
+#include <osmocom/gsmtap_util.h>
+
+/* somewhere in 05.08 */
+#define MAX_CELLS_IN_BA 32
+
+/* Information about a single cell / BCCH */
+struct cell_info {
+ struct llist_head list;
+
+ uint16_t band_arfcn;
+ uint8_t bsic;
+ uint8_t rxlev;
+
+ struct {
+ uint16_t mcc; /* Mobile Country Code */
+ uint16_t mnc; /* Mobile Network Code */
+ uint16_t lac; /* Location Area Code */
+ uint16_t rac; /* Routing Area Code */
+ uint16_t cid; /* Cell ID */
+ } id;
+ uint16_t ba_arfcn[MAX_CELLS_IN_BA];
+ uint8_t ba_arfcn_num;
+
+ struct {
+ int32_t fn_delta; /* delta to current L1 fn */
+ int16_t qbit_delta;
+ int16_t afc_delta;
+ } l1_sync;
+};
+
+#define AFS_F_PM_DONE 0x01
+#define AFS_F_TESTED 0x02
+#define AFS_F_BCCH 0x04
+struct arfcn_state {
+ uint8_t rxlev;
+ uint8_t flags;
+};
+
+enum bscan_state {
+ BSCAN_S_NONE,
+ BSCAN_S_WAIT_DATA,
+ BSCAN_S_DONE,
+};
+
+enum fps_state {
+ FPS_S_NONE,
+ FPS_S_PM_GSM900,
+ FPS_S_PM_EGSM900,
+ FPS_S_PM_GSM1800,
+ FPS_S_BINFO,
+};
+
+struct full_power_scan {
+ /* Full Power Scan */
+ enum fps_state fps_state;
+ struct arfcn_state arfcn_state[1024];
+
+ struct osmocom_ms *ms;
+
+ /* BCCH info part */
+ enum bscan_state state;
+ struct llist_head cell_list;
+ struct cell_info *cur_cell;
+ uint16_t cur_arfcn;
+ struct timer_list timer;
+};
+
+static struct full_power_scan fps;
+
+static int get_next_arfcn(struct full_power_scan *fps)
+{
+ unsigned int i;
+ uint8_t best_rxlev = 0;
+ int best_arfcn = -1;
+
+ for (i = 0; i < ARRAY_SIZE(fps->arfcn_state); i++) {
+ struct arfcn_state *af = &fps->arfcn_state[i];
+ /* skip ARFCN's where we don't have a PM */
+ if (!(af->flags & AFS_F_PM_DONE))
+ continue;
+ /* skip ARFCN's that we already tested */
+ if (af->flags & AFS_F_TESTED)
+ continue;
+ /* if current arfcn_state is better than best so far,
+ * select it */
+ if (af->rxlev > best_rxlev) {
+ best_rxlev = af->rxlev;
+ best_arfcn = i;
+ }
+ }
+ printf("arfcn=%d rxlev=%u\n", best_arfcn, best_rxlev);
+ return best_arfcn;
+}
+
+static struct cell_info *cell_info_alloc(void)
+{
+ struct cell_info *ci = talloc_zero(NULL, struct cell_info);
+ return ci;
+}
+
+static void cell_info_free(struct cell_info *ci)
+{
+ talloc_free(ci);
+}
+
+/* start to scan for one ARFCN */
+static int _cinfo_start_arfcn(unsigned int band_arfcn)
+{
+ int rc;
+
+ /* ask L1 to try to tune to new ARFCN */
+ /* FIXME: decode band */
+ rc = l1ctl_tx_fbsb_req(fps.ms, band_arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0, CCCH_MODE_COMBINED);
+ if (rc < 0)
+ return rc;
+
+ /* allocate new cell info structure */
+ fps.cur_cell = cell_info_alloc();
+ fps.cur_arfcn = band_arfcn;
+ fps.cur_cell->band_arfcn = band_arfcn;
+ /* FIXME: start timer in case we never get a sync */
+ fps.state = BSCAN_S_WAIT_DATA;
+ bsc_schedule_timer(&fps.timer, 2, 0);
+
+ return 0;
+}
+
+
+static void cinfo_next_cell(void *data)
+{
+ int rc;
+
+ /* we've been waiting for BCCH info */
+ fps.arfcn_state[fps.cur_arfcn].flags |= AFS_F_TESTED;
+ /* if there is a BCCH, we need to add the collected BCCH
+ * information to our list */
+
+ if (fps.arfcn_state[fps.cur_arfcn].flags & AFS_F_BCCH)
+ llist_add(&fps.cur_cell->list, &fps.cell_list);
+ else
+ cell_info_free(fps.cur_cell);
+
+ rc = get_next_arfcn(&fps);
+ if (rc < 0) {
+ fps.state = BSCAN_S_DONE;
+ return;
+ }
+ /* start syncing to the next ARFCN */
+ _cinfo_start_arfcn(rc);
+}
+
+static void cinfo_timer_cb(void *data)
+{
+ switch (fps.state) {
+ case BSCAN_S_WAIT_DATA:
+ cinfo_next_cell(data);
+ break;
+ }
+}
+
+/* Update cell_info for current cell with received BCCH info */
+static int rx_bcch_info(const uint8_t *data)
+{
+ struct cell_info *ci = fps.cur_cell;
+ struct gsm48_system_information_type_header *si_hdr;
+ si_hdr = (struct gsm48_system_information_type_header *) data;;
+
+ /* we definitely have a BCCH on this channel */
+ fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH;
+
+ switch (si_hdr->system_information) {
+ case GSM48_MT_RR_SYSINFO_1:
+ /* FIXME: CA, RACH control */
+ break;
+ case GSM48_MT_RR_SYSINFO_2:
+ /* FIXME: BA, NCC, RACH control */
+ break;
+ case GSM48_MT_RR_SYSINFO_3:
+ /* FIXME: cell_id, LAI */
+ break;
+ case GSM48_MT_RR_SYSINFO_4:
+ /* FIXME: LAI */
+ break;
+ }
+ return 0;
+}
+
+/* Update L1/SCH information (AFC/QBIT/FN offset, BSIC) */
+static int rx_sch_info()
+{
+ /* FIXME */
+}
+
+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;
+ int rc;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_PM_RES:
+ mr = signal_data;
+ /* check if PM result is for same MS */
+ if (fps.ms != mr->ms)
+ return 0;
+ arfcn = mr->band_arfcn & 0x3ff;
+ /* update RxLev and notice that PM was done */
+ fps.arfcn_state[arfcn].rxlev = mr->rx_lev;
+ fps.arfcn_state[arfcn].flags |= AFS_F_PM_DONE;
+ break;
+ case S_L1CTL_PM_DONE:
+ ms = signal_data;
+ switch (fps.fps_state) {
+ case FPS_S_PM_GSM900:
+ fps.fps_state = FPS_S_PM_EGSM900;
+ return l1ctl_tx_pm_req_range(ms, 955, 1023);
+ case FPS_S_PM_EGSM900:
+ fps.fps_state = FPS_S_PM_GSM1800;
+ return l1ctl_tx_pm_req_range(ms, 512, 885);
+ case FPS_S_PM_GSM1800:
+ /* power measurement has finished, we can start
+ * to actually iterate over the ARFCN's and try
+ * to sync to BCCHs */
+ fps.fps_state = FPS_S_BINFO;
+ rc = get_next_arfcn(&fps);
+ if (rc < 0) {
+ fps.state = BSCAN_S_DONE;
+ return;
+ }
+ _cinfo_start_arfcn(rc);
+ break;
+ }
+ break;
+ case S_L1CTL_FBSB_RESP:
+ /* We actually got a FCCH/SCH burst */
+#if 0
+ fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH;
+ /* fallthrough */
+#else
+ break;
+#endif
+ case S_L1CTL_FBSB_ERR:
+ /* We timed out, move on */
+ if (fps.state == BSCAN_S_WAIT_DATA)
+ cinfo_next_cell(NULL);
+ break;
+ }
+ return 0;
+}
+
+/* start the full power scan */
+int fps_start(struct osmocom_ms *ms)
+{
+ memset(&fps, 0, sizeof(fps));
+ fps.ms = ms;
+
+ fps.timer.cb = cinfo_timer_cb;
+ fps.timer.data = &fps;
+
+ /* Start by scanning the good old GSM900 band */
+ fps.fps_state = FPS_S_PM_GSM900;
+ return l1ctl_tx_pm_req_range(ms, 0, 124);
+}
+
+int fps_init(void)
+{
+ return register_signal_handler(SS_L1CTL, &bscan_sig_cb, NULL);
+}
diff --git a/src/host/layer23/src/gsm322.c b/src/host/layer23/src/gsm322.c
new file mode 100644
index 00000000..94e9caa2
--- /dev/null
+++ b/src/host/layer23/src/gsm322.c
@@ -0,0 +1,3497 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/utils.h>
+#include <osmocore/gsm48.h>
+#include <osmocore/signal.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/file.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/networks.h>
+#include <osmocom/vty.h>
+
+extern void *l23_ctx;
+
+static void gsm322_cs_timeout(void *arg);
+static void gsm322_cs_loss(void *arg);
+static int gsm322_cs_select(struct osmocom_ms *ms, int any);
+static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg);
+
+#warning HACKING!!!
+int hack;
+
+/*
+ * notes
+ */
+
+/* Cell selection process
+ *
+ * The process depends on states and events (finites state machine).
+ *
+ * During states of cell selection or cell re-selection, the search for a cell
+ * is performed in two steps:
+ *
+ * 1. Measurement of received level of all relevant frequencies (rx-lev)
+ *
+ * 2. Receive system information messages of all relevant frequencies
+ *
+ * During this process, the results are stored in a list of all frequencies.
+ * This list is checked whenever a cell is selected. It depends on the results
+ * if the cell is 'suitable' and 'allowable' to 'camp' on.
+ *
+ * This list is also used to generate a list of available networks.
+ *
+ * The states are:
+ *
+ * - cs->list[0..1023].xxx for each cell, where
+ * - flags and rxlev_db are used to store outcome of cell scanning process
+ * - sysinfo pointing to sysinfo memory, allocated temporarily
+ * - cs->selected and cs->sel_* states of the current / last selected cell.
+ *
+ *
+ * There is a special state: GSM322_PLMN_SEARCH
+ * It is used to search for all cells, to find the HPLMN. This is triggered
+ * by a timer. Also it is used before selecting PLMN from list.
+ *
+ */
+
+/* PLMN selection process
+ *
+ * The PLMN (Public Land Mobile Network = Operator's Network) has two different
+ * search processes:
+ *
+ * 1. Automatic search
+ *
+ * 2. Manual search
+ *
+ * The process depends on states and events (finites state machine).
+ *
+ */
+
+/* File format of BA list:
+ *
+ * uint16_t mcc
+ * uint16_t mcc
+ * uint8_t freq[128];
+ * where frequency 0 is bit 0 of first byte
+ *
+ * If not end-of-file, the next BA list is stored.
+ */
+
+/* List of lists:
+ *
+ * * subscr->plmn_list
+ *
+ * The "PLMN Selector list" stores prefered networks to select during PLMN
+ * search process. This list is also stored in the SIM.
+ *
+ * * subscr->plmn_na
+ *
+ * The "forbidden PLMNs" list stores all networks that rejected us. The stored
+ * network will not be used when searching PLMN automatically. This list is
+ * also stored din the SIM.
+ *
+ * * plmn->forbidden_la
+ *
+ * The "forbidden LAs for roaming" list stores all location areas where roaming
+ * was not allowed.
+ *
+ * * cs->list[1024]
+ *
+ * This list stores measurements and cell informations during cell selection
+ * process. It can be used to speed up repeated cell selection.
+ *
+ * * cs->ba_list
+ *
+ * This list stores a map of frequencies used for a PLMN. If this lists exists
+ * for a PLMN, it helps to speedup cell scan process.
+ *
+ * * plmn->sorted_plmn
+ *
+ * This list is generated whenever a PLMN search is started and a list of PLMNs
+ * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found
+ * during scan process.
+ */
+
+/*
+ * event messages
+ */
+
+static const struct value_string gsm322_event_names[] = {
+ { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" },
+ { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" },
+ { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" },
+ { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" },
+ { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" },
+ { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" },
+ { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" },
+ { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" },
+ { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" },
+ { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" },
+ { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" },
+ { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" },
+ { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" },
+ { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" },
+ { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" },
+ { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" },
+ { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" },
+ { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" },
+ { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" },
+ { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" },
+ { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" },
+ { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" },
+ { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" },
+ { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" },
+ { 0, NULL }
+};
+
+const char *get_event_name(int value)
+{
+ return get_value_string(gsm322_event_names, value);
+}
+
+
+/* allocate a 03.22 event message */
+struct msgb *gsm322_msgb_alloc(int msg_type)
+{
+ struct msgb *msg;
+ struct gsm322_msg *gm;
+
+ msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event");
+ if (!msg)
+ return NULL;
+
+ gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm));
+ gm->msg_type = msg_type;
+
+ return msg;
+}
+
+/* queue PLMN selection message */
+int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+
+ msgb_enqueue(&plmn->event_queue, msg);
+
+ return 0;
+}
+
+/* queue cell selection message */
+int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ msgb_enqueue(&cs->event_queue, msg);
+
+ return 0;
+}
+
+/*
+ * support
+ */
+
+static int gsm322_sync_to_cell(struct gsm322_cellsel *cs)
+{
+ struct osmocom_ms *ms = cs->ms;
+ struct gsm48_sysinfo *s = cs->si;
+
+ cs->ccch_state = GSM322_CCCH_ST_INIT;
+ if (s) {
+ if (s->ccch_conf == 1) {
+ LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode COMB\n");
+ cs->ccch_mode = CCCH_MODE_COMBINED;
+ } else {
+ LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode NON-COMB\n");
+ cs->ccch_mode = CCCH_MODE_NON_COMBINED;
+ }
+ } else {
+ LOGP(DCS, LOGL_INFO, "No sysinfo, ccch mode NONE\n");
+ cs->ccch_mode = CCCH_MODE_NONE;
+ }
+// printf("s->ccch_conf %d\n", cs->si->ccch_conf);
+
+// l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL);
+ return l1ctl_tx_fbsb_req(ms, cs->arfcn,
+ L1CTL_FBSB_F_FB01SB, 100, 0,
+ cs->ccch_mode);
+}
+
+static void gsm322_unselect_cell(struct gsm322_cellsel *cs)
+{
+ cs->selected = 0;
+ cs->si = NULL;
+ memset(&cs->sel_si, 0, sizeof(cs->sel_si));
+ cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0;
+}
+
+/* print to DCS logging */
+static void print_dcs(void *priv, const char *fmt, ...)
+{
+ char buffer[1000];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ if (buffer[0])
+// LOGP(DCS, LOGL_INFO, "%s", buffer);
+ printf("%s", buffer);
+}
+
+/* del forbidden LA */
+int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
+ uint16_t mnc, uint16_t lac)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_la_list *la;
+
+ llist_for_each_entry(la, &plmn->forbidden_la, entry) {
+ if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) {
+ LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
+ "LAs (mcc=%s, mnc=%s, lac=%04x)\n",
+ gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac);
+ llist_del(&la->entry);
+ talloc_free(la);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* add forbidden LA */
+int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc,
+ uint16_t mnc, uint16_t lac, uint8_t cause)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_la_list *la;
+
+ LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs "
+ "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc),
+ gsm_print_mnc(mnc), lac);
+ la = talloc_zero(l23_ctx, struct gsm322_la_list);
+ if (!la)
+ return -ENOMEM;
+ la->mcc = mcc;
+ la->mnc = mnc;
+ la->lac = lac;
+ la->cause = cause;
+ llist_add_tail(&la->entry, &plmn->forbidden_la);
+
+ return 0;
+}
+
+/* search forbidden LA */
+int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc,
+ uint16_t lac)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_la_list *la;
+
+ llist_for_each_entry(la, &plmn->forbidden_la, entry) {
+ if (la->mcc == mcc && la->mnc == mnc && la->lac == lac)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* search for PLMN in all BA lists */
+static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs,
+ uint16_t mcc, uint16_t mnc)
+{
+ struct gsm322_ba_list *ba, *ba_found = NULL;
+
+ /* search for BA list */
+ llist_for_each_entry(ba, &cs->ba_list, entry) {
+ if (ba->mcc == mcc
+ && ba->mnc == mnc) {
+ ba_found = ba;
+ break;
+ }
+ }
+
+ return ba_found;
+}
+
+/* search available PLMN */
+int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc)
+{
+ int i;
+
+ for (i = 0; i <= 1023; i++) {
+ if (cs->list[i].sysinfo
+ && cs->list[i].sysinfo->mcc == mcc
+ && cs->list[i].sysinfo->mnc == mnc)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* search available HPLMN */
+int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi)
+{
+ int i;
+
+ for (i = 0; i <= 1023; i++) {
+ if (cs->list[i].sysinfo
+ && gsm_match_mnc(cs->list[i].sysinfo->mcc,
+ cs->list[i].sysinfo->mnc, imsi))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* del forbidden LA */
+/*
+ * timer
+ */
+
+/*plmn search timer event */
+static void plmn_timer_timeout(void *arg)
+{
+ struct gsm322_plmn *plmn = arg;
+ struct msgb *nmsg;
+
+ LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n");
+
+ /* indicate PLMN selection T timeout */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH);
+ if (!nmsg)
+ return;
+ gsm322_plmn_sendmsg(plmn->ms, nmsg);
+}
+
+/* start plmn search timer */
+static void start_plmn_timer(struct gsm322_plmn *plmn, int secs)
+{
+ LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n",
+ secs / 60);
+ plmn->timer.cb = plmn_timer_timeout;
+ plmn->timer.data = plmn;
+ bsc_schedule_timer(&plmn->timer, secs, 0);
+}
+
+/* stop plmn search timer */
+static void stop_plmn_timer(struct gsm322_plmn *plmn)
+{
+ if (bsc_timer_pending(&plmn->timer)) {
+ LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n");
+ bsc_del_timer(&plmn->timer);
+ }
+}
+
+/* start cell selection timer */
+void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro)
+{
+ LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec);
+ cs->timer.cb = gsm322_cs_timeout;
+ cs->timer.data = cs;
+ bsc_schedule_timer(&cs->timer, sec, micro);
+}
+
+/* start loss timer */
+void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro)
+{
+ /* update timer */
+ cs->timer.cb = gsm322_cs_loss;
+ cs->timer.data = cs;
+ if (bsc_timer_pending(&cs->timer)) {
+ struct timeval current_time;
+ unsigned long long currentTime;
+
+ gettimeofday(&current_time, NULL);
+ currentTime = current_time.tv_sec * 1000000LL
+ + current_time.tv_usec;
+ currentTime += sec * 1000000LL + micro;
+ cs->timer.timeout.tv_sec = currentTime / 1000000LL;
+ cs->timer.timeout.tv_usec = currentTime % 1000000LL;
+
+ return;
+ }
+
+ LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec);
+ bsc_schedule_timer(&cs->timer, sec, micro);
+}
+
+/* stop cell selection timer */
+static void stop_cs_timer(struct gsm322_cellsel *cs)
+{
+ if (bsc_timer_pending(&cs->timer)) {
+ LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n");
+ bsc_del_timer(&cs->timer);
+ }
+}
+
+/*
+ * state change
+ */
+
+const char *plmn_a_state_names[] = {
+ "A0 null",
+ "A1 trying RPLMN",
+ "A2 on PLMN",
+ "A3 trying PLMN",
+ "A4 wait for PLMN to appear",
+ "A5 HPLMN search",
+ "A6 no SIM inserted"
+};
+
+const char *plmn_m_state_names[] = {
+ "M0 null",
+ "M1 trying RPLMN",
+ "M2 on PLMN",
+ "M3 not on PLMN",
+ "M4 trying PLMN",
+ "M5 no SIM inserted"
+};
+
+const char *cs_state_names[] = {
+ "C0 null",
+ "C1 normal cell selection",
+ "C2 stored cell selection",
+ "C3 camped normally",
+ "C4 normal cell re-selection",
+ "C5 choose cell",
+ "C6 any cell selection",
+ "C7 camped on any cell",
+ "C8 any cell re-selection",
+ "C9 choose any cell",
+ "PLMN search",
+ "HPLMN search"
+};
+
+
+/* new automatic PLMN search state */
+static void new_a_state(struct gsm322_plmn *plmn, int state)
+{
+ if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) {
+ LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n");
+ return;
+ }
+
+ stop_plmn_timer(plmn);
+
+ if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *)))
+ return;
+
+ LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
+ plmn_a_state_names[plmn->state], plmn_a_state_names[state]);
+
+ plmn->state = state;
+}
+
+/* new manual PLMN search state */
+static void new_m_state(struct gsm322_plmn *plmn, int state)
+{
+ if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) {
+ LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n");
+ return;
+ }
+
+ if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *)))
+ return;
+
+ LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n",
+ plmn_m_state_names[plmn->state], plmn_m_state_names[state]);
+
+ plmn->state = state;
+}
+
+/* new Cell selection state */
+static void new_c_state(struct gsm322_cellsel *cs, int state)
+{
+ if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *)))
+ return;
+
+ LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n",
+ cs_state_names[cs->state], cs_state_names[state]);
+
+ /* stop cell selection timer, if running */
+ stop_cs_timer(cs);
+
+ /* stop scanning of power measurement */
+ if (cs->powerscan) {
+#ifdef TODO
+ stop power scanning
+#endif
+ cs->powerscan = 0;
+ }
+
+ cs->state = state;
+}
+
+/*
+ * list of PLMNs
+ */
+
+/* 4.4.3 create sorted list of PLMN
+ *
+ * the source of entries are
+ *
+ * - HPLMN
+ * - entries found in the SIM's PLMN Selector list
+ * - scanned PLMNs above -85 dB (random order)
+ * - scanned PLMNs below or equal -85 (by received level)
+ *
+ * NOTE:
+ *
+ * The list only includes networks found at last scan.
+ *
+ * The list always contains HPLMN if available, even if not used by PLMN
+ * search process at some conditions.
+ *
+ * The list contains all PLMNs even if not allowed, so entries have to be
+ * removed when selecting from the list. (In case we use manual cell selection,
+ * we need to provide non-allowed networks also.)
+ */
+static int gsm322_sort_list(struct osmocom_ms *ms)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm_sub_plmn_list *sim_entry;
+ struct gsm_sub_plmn_na *na_entry;
+ struct llist_head temp_list;
+ struct gsm322_plmn_list *temp, *found;
+ struct llist_head *lh, *lh2;
+ int i, entries, move;
+ int8_t search_db = 0;
+
+ /* flush list */
+ llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
+ llist_del(lh);
+ talloc_free(lh);
+ }
+
+ /* Create a temporary list of all networks */
+ INIT_LLIST_HEAD(&temp_list);
+ for (i = 0; i <= 1023; i++) {
+ if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA)
+ || !cs->list[i].sysinfo)
+ continue;
+
+ /* search if network has multiple cells */
+ found = NULL;
+ llist_for_each_entry(temp, &temp_list, entry) {
+ if (temp->mcc == cs->list[i].sysinfo->mcc
+ && temp->mnc == cs->list[i].sysinfo->mnc) {
+ found = temp;
+ break;
+ }
+ }
+ /* update or create */
+ if (found) {
+ if (cs->list[i].rxlev_db > found->rxlev_db)
+ found->rxlev_db = cs->list[i].rxlev_db;
+ } else {
+ temp = talloc_zero(l23_ctx, struct gsm322_plmn_list);
+ if (!temp)
+ return -ENOMEM;
+ temp->mcc = cs->list[i].sysinfo->mcc;
+ temp->mnc = cs->list[i].sysinfo->mnc;
+ temp->rxlev_db = cs->list[i].rxlev_db;
+ llist_add_tail(&temp->entry, &temp_list);
+ }
+ }
+
+ /* move Home PLMN, if in list, else add it */
+ if (subscr->sim_valid) {
+ found = NULL;
+ llist_for_each_entry(temp, &temp_list, entry) {
+ if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) {
+ found = temp;
+ break;
+ }
+ }
+ if (found) {
+ /* move */
+ llist_del(&found->entry);
+ llist_add_tail(&found->entry, &plmn->sorted_plmn);
+ }
+ }
+
+ /* move entries if in SIM's PLMN Selector list */
+ llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) {
+ found = NULL;
+ llist_for_each_entry(temp, &temp_list, entry) {
+ if (temp->mcc == sim_entry->mcc
+ && temp->mnc == sim_entry->mnc) {
+ found = temp;
+ break;
+ }
+ }
+ if (found) {
+ llist_del(&found->entry);
+ llist_add_tail(&found->entry, &plmn->sorted_plmn);
+ }
+ }
+
+ /* move PLMN above -85 dBm in random order */
+ entries = 0;
+ llist_for_each_entry(temp, &temp_list, entry) {
+ if (temp->rxlev_db > -85)
+ entries++;
+ }
+ while(entries) {
+ move = random() % entries;
+ i = 0;
+ llist_for_each_entry(temp, &temp_list, entry) {
+ if (temp->rxlev_db > -85) {
+ if (i == move) {
+ llist_del(&temp->entry);
+ llist_add_tail(&temp->entry,
+ &plmn->sorted_plmn);
+ break;
+ }
+ i++;
+ }
+ }
+ entries--;
+ }
+
+ /* move ohter PLMN in decreasing order */
+ while(1) {
+ found = NULL;
+ llist_for_each_entry(temp, &temp_list, entry) {
+ if (!found
+ || temp->rxlev_db > search_db) {
+ search_db = temp->rxlev_db;
+ found = temp;
+ }
+ }
+ if (!found)
+ break;
+ llist_del(&found->entry);
+ llist_add_tail(&found->entry, &plmn->sorted_plmn);
+ }
+
+ /* mark forbidden PLMNs, if in list of forbidden networks */
+ i = 0;
+ llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
+ llist_for_each_entry(na_entry, &subscr->plmn_na, entry) {
+ if (temp->mcc == na_entry->mcc
+ && temp->mnc == na_entry->mnc) {
+ temp->cause = na_entry->cause;
+ break;
+ }
+ }
+ LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. "
+ "(%02d: mcc=%s mnc=%s allowed=%s rx-lev=%d)\n",
+ i, gsm_print_mcc(temp->mcc),
+ gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes",
+ temp->rxlev_db);
+ i++;
+ }
+
+ gsm322_dump_sorted_plmn(ms);
+
+ return 0;
+}
+
+/*
+ * handler for automatic search
+ */
+
+/* go On PLMN state */
+static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ new_a_state(plmn, GSM322_A2_ON_PLMN);
+
+ /* start timer, if on VPLMN of home country OR special case */
+ if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi)
+ && (subscr->always_search_hplmn
+ || gsm_match_mcc(plmn->mcc, subscr->imsi))) {
+ if (subscr->sim_valid && subscr->t6m_hplmn)
+ start_plmn_timer(plmn, subscr->t6m_hplmn * 360);
+ else
+ start_plmn_timer(plmn, 30 * 360);
+ } else
+ stop_plmn_timer(plmn);
+
+ return 0;
+}
+
+/* indicate selected PLMN */
+static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Selected Network: %s, %s\n",
+ gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ return gsm322_a_go_on_plmn(ms, msg);
+}
+
+/* no (more) PLMN in list */
+static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct msgb *nmsg;
+ int found;
+
+ /* any allowable PLMN available? */
+ plmn->mcc = plmn->mnc = 0;
+ found = gsm322_cs_select(ms, 0);
+
+ /* if no PLMN in list */
+ if (found < 0) {
+ LOGP(DPLMN, LOGL_INFO, "Not any PLNs allowable.\n");
+
+ new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
+
+ /* we must forward this, otherwhise "Any cell selection"
+ * will not start automatically.
+ */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* select first PLMN in list */
+ plmn->mcc = cs->list[found].sysinfo->mcc;
+ plmn->mnc = cs->list[found].sysinfo->mnc;
+
+ LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n",
+ gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
+ gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ /* go On PLMN */
+ return gsm322_a_indicate_selected(ms, msg);
+}
+
+/* select first PLMN in list */
+static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+ struct gsm322_plmn_list *plmn_entry;
+ struct gsm322_plmn_list *plmn_first = NULL;
+ int i;
+
+ /* generate list */
+ gsm322_sort_list(ms);
+
+ /* select first entry */
+ i = 0;
+ llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
+ /* if last selected PLMN was HPLMN, we skip that */
+ if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc,
+ subscr->imsi)
+ && plmn_entry->mcc == plmn->mcc
+ && plmn_entry->mnc == plmn->mnc) {
+ LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was "
+ "previously selected.\n");
+ i++;
+ continue;
+ }
+ /* select first allowed network */
+ if (!plmn_entry->cause) {
+ plmn_first = plmn_entry;
+ break;
+ }
+ LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
+ "not allowed (cause %d).\n", plmn_entry->mcc,
+ plmn_entry->mnc, plmn_entry->cause);
+ i++;
+ }
+ plmn->plmn_curr = i;
+
+ /* if no PLMN in list */
+ if (!plmn_first) {
+ LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n");
+ gsm322_a_no_more_plmn(ms, msg);
+
+ return 0;
+ }
+
+ LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
+ "mnc=%s %s, %s)\n", plmn->plmn_curr,
+ gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc),
+ gsm_get_mcc(plmn_first->mcc),
+ gsm_get_mnc(plmn_first->mcc, plmn_first->mnc));
+
+ /* set current network */
+ plmn->mcc = plmn_first->mcc;
+ plmn->mnc = plmn_first->mnc;
+
+ new_a_state(plmn, GSM322_A3_TRYING_PLMN);
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* select next PLMN in list */
+static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct msgb *nmsg;
+ struct gsm322_plmn_list *plmn_entry;
+ struct gsm322_plmn_list *plmn_next = NULL;
+ int i, ii;
+
+ /* select next entry from list */
+ i = 0;
+ ii = plmn->plmn_curr + 1;
+ llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
+ /* skip previously selected networks */
+ if (i < ii) {
+ i++;
+ continue;
+ }
+ /* select next allowed network */
+ if (!plmn_entry->cause) {
+ plmn_next = plmn_entry;
+ break;
+ }
+ LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it "
+ "not allowed (cause %d).\n", plmn_entry->mcc,
+ plmn_entry->mnc, plmn_entry->cause);
+ i++;
+ }
+ plmn->plmn_curr = i;
+
+ /* if no more PLMN in list */
+ if (!plmn_next) {
+ LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n");
+ gsm322_a_no_more_plmn(ms, msg);
+ return 0;
+ }
+
+
+ LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s "
+ "mnc=%s %s, %s)\n", plmn->plmn_curr,
+ gsm_print_mcc(plmn_next->mcc), gsm_print_mnc(plmn_next->mnc),
+ gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ /* set next network */
+ plmn->mcc = plmn_next->mcc;
+ plmn->mnc = plmn_next->mnc;
+
+ new_a_state(plmn, GSM322_A3_TRYING_PLMN);
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* User re-selection event */
+static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_plmn_list *plmn_entry;
+ struct gsm322_plmn_list *plmn_found = NULL;
+
+ if (!subscr->sim_valid) {
+ return 0;
+ }
+
+ /* try again later, if not idle */
+ if (rr->state != GSM48_RR_ST_IDLE) {
+ LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
+
+ return 0;
+ }
+
+ /* search current PLMN in list */
+ llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) {
+ if (plmn_entry->mcc == plmn->mcc
+ && plmn_entry->mnc == plmn->mnc)
+ plmn_found = plmn_entry;
+ break;
+ }
+
+ /* abort if list is empty */
+ if (!plmn_found) {
+ LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n");
+ return 0;
+ }
+
+ LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list "
+ "and restarting PLMN search process.\n");
+
+ /* move entry to end of list */
+ llist_del(&plmn_found->entry);
+ llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn);
+
+ /* select first PLMN in list */
+ return gsm322_a_sel_first_plmn(ms, msg);
+}
+
+/* PLMN becomes available */
+static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+
+ if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc
+ && subscr->plmn_mnc == gm->mnc) {
+ /* go On PLMN */
+ plmn->mcc = gm->mcc;
+ plmn->mnc = gm->mnc;
+ LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n");
+ return gsm322_a_go_on_plmn(ms, msg);
+ } else {
+ /* select first PLMN in list */
+ LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN "
+ "search process.\n");
+ return gsm322_a_sel_first_plmn(ms, msg);
+ }
+}
+
+/* loss of radio coverage */
+static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int found;
+ struct msgb *nmsg;
+
+ /* any PLMN available */
+ found = gsm322_cs_select(ms, 0);
+
+ /* if PLMN in list */
+ if (found >= 0) {
+ LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s "
+ "%s, %s)\n", gsm_print_mcc(
+ cs->list[found].sysinfo->mcc),
+ gsm_print_mnc(cs->list[found].sysinfo->mnc),
+ gsm_get_mcc(cs->list[found].sysinfo->mcc),
+ gsm_get_mnc(cs->list[found].sysinfo->mcc,
+ cs->list[found].sysinfo->mnc));
+ return gsm322_a_sel_first_plmn(ms, msg);
+ }
+
+ LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n");
+
+ plmn->mcc = plmn->mnc = 0;
+
+ new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN);
+
+ /* Tell cell selection process to handle "no cell found". */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* MS is switched on OR SIM is inserted OR removed */
+static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct msgb *nmsg;
+
+ if (!subscr->sim_valid) {
+ LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
+ LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
+ new_a_state(plmn, GSM322_A6_NO_SIM);
+
+ return 0;
+ }
+
+ /* if there is a registered PLMN */
+ if (subscr->plmn_valid) {
+ /* select the registered PLMN */
+ plmn->mcc = subscr->plmn_mcc;
+ plmn->mnc = subscr->plmn_mnc;
+
+ LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
+ "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
+ gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+ LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
+ "%s, %s)\n", gsm_print_mcc(plmn->mcc),
+ gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ new_a_state(plmn, GSM322_A1_TRYING_RPLMN);
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* initiate search at cell selection */
+ LOGP(DSUM, LOGL_INFO, "Search for network\n");
+ LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* MS is switched off */
+static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+
+ new_a_state(plmn, GSM322_A0_NULL);
+
+ return 0;
+}
+
+static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
+{
+ LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
+ return 0;
+}
+
+/* SIM is removed */
+static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* indicate SIM remove to cell selection process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return gsm322_a_switch_on(ms, msg);
+}
+
+/* location update response: "Roaming not allowed" */
+static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* store in list of forbidden LAs is done in gsm48* */
+
+ return gsm322_a_sel_first_plmn(ms, msg);
+}
+
+/* On VPLMN of home country and timeout occurs */
+static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct msgb *nmsg;
+
+ /* try again later, if not idle and not camping */
+ if (rr->state != GSM48_RR_ST_IDLE
+ || cs->state != GSM322_C3_CAMPED_NORMALLY) {
+ LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n");
+ start_plmn_timer(plmn, 60);
+
+ return 0;
+ }
+
+ new_a_state(plmn, GSM322_A5_HPLMN_SEARCH);
+
+ /* initiate search at cell selection */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* manual mode selected */
+static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* restart state machine */
+ gsm322_a_switch_off(ms, msg);
+ ms->settings.plmn_mode = PLMN_MODE_MANUAL;
+ gsm322_m_switch_on(ms, msg);
+
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(ms, nmsg);
+
+ return 0;
+}
+
+/*
+ * handler for manual search
+ */
+
+/* display PLMNs and to Not on PLMN */
+static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+ int msg_type = gm->msg_type;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm_sub_plmn_list *temp;
+
+ /* generate list */
+ gsm322_sort_list(ms);
+
+ vty_notify(ms, NULL);
+ switch (msg_type) {
+ case GSM322_EVENT_REG_FAILED:
+ vty_notify(ms, "Failed to register to network %s, %s "
+ "(%s, %s)\n",
+ gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
+ gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+ break;
+ case GSM322_EVENT_NO_CELL_FOUND:
+ vty_notify(ms, "No cell found for network %s, %s "
+ "(%s, %s)\n",
+ gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
+ gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+ break;
+ case GSM322_EVENT_ROAMING_NA:
+ vty_notify(ms, "Roaming not allowed to network %s, %s "
+ "(%s, %s)\n",
+ gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
+ gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+ break;
+ }
+
+ if (llist_empty(&plmn->sorted_plmn))
+ vty_notify(ms, "Search network!\n");
+ else {
+ vty_notify(ms, "Search or select from network:\n");
+ llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
+ vty_notify(ms, " Network %s, %s (%s, %s)\n",
+ gsm_print_mcc(temp->mcc),
+ gsm_print_mnc(temp->mnc),
+ gsm_get_mcc(temp->mcc),
+ gsm_get_mnc(temp->mcc, temp->mnc));
+ }
+
+ /* go Not on PLMN state */
+ new_m_state(plmn, GSM322_M3_NOT_ON_PLMN);
+
+ return 0;
+}
+
+/* user starts reselection */
+static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+
+ if (!subscr->sim_valid) {
+ return 0;
+ }
+
+ /* try again later, if not idle */
+ if (rr->state != GSM48_RR_ST_IDLE) {
+ LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n");
+
+ return 0;
+ }
+
+ /* initiate search at cell selection */
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Searching Network, please wait...\n");
+ LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n");
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* MS is switched on OR SIM is inserted OR removed */
+static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct msgb *nmsg;
+
+ if (!subscr->sim_valid) {
+ LOGP(DSUM, LOGL_INFO, "SIM is removed\n");
+ LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n");
+ new_m_state(plmn, GSM322_M5_NO_SIM);
+
+ return 0;
+ }
+
+ /* if there is a registered PLMN */
+ if (subscr->plmn_valid) {
+ struct msgb *nmsg;
+
+ /* select the registered PLMN */
+ plmn->mcc = subscr->plmn_mcc;
+ plmn->mnc = subscr->plmn_mnc;
+
+ LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN "
+ "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc),
+ gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+ LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s "
+ "%s, %s)\n", gsm_print_mcc(plmn->mcc),
+ gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc),
+ gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* initiate search at cell selection */
+ LOGP(DSUM, LOGL_INFO, "Search for network\n");
+ LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n");
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Searching Network, please wait...\n");
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* MS is switched off */
+static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+
+ stop_plmn_timer(plmn);
+
+ new_m_state(plmn, GSM322_M0_NULL);
+
+ return 0;
+}
+
+static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg)
+{
+ LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n");
+ return 0;
+}
+
+/* SIM is removed */
+static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct msgb *nmsg;
+
+ stop_plmn_timer(plmn);
+
+ /* indicate SIM remove to cell selection process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return gsm322_m_switch_on(ms, msg);
+}
+
+/* go to On PLMN state */
+static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ /* if selected PLMN is in list of forbidden PLMNs */
+ gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc);
+
+ /* set last registered PLMN */
+ subscr->plmn_valid = 1;
+ subscr->plmn_mcc = plmn->mcc;
+ subscr->plmn_mnc = plmn->mnc;
+#ifdef TODO
+ store on sim
+#endif
+
+ new_m_state(plmn, GSM322_M2_ON_PLMN);
+
+ return 0;
+}
+
+/* indicate selected PLMN */
+static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Selected Network: %s, %s\n",
+ gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ return gsm322_m_go_on_plmn(ms, msg);
+}
+
+/* previously selected PLMN becomes available again */
+static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ new_m_state(plmn, GSM322_M1_TRYING_RPLMN);
+
+ if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) {
+ struct msgb *nmsg;
+
+ LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not "
+ "selected, so start selection.\n");
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+ }
+
+ return 0;
+}
+
+/* the user has selected given PLMN */
+static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+ struct msgb *nmsg;
+
+ /* use user selection */
+ plmn->mcc = gm->mcc;
+ plmn->mnc = gm->mnc;
+
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Selected Network: %s, %s\n",
+ gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+ LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s "
+ "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc),
+ gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc));
+
+ new_m_state(plmn, GSM322_M4_TRYING_PLMN);
+
+ /* indicate New PLMN */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* auto mode selected */
+static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* restart state machine */
+ gsm322_m_switch_off(ms, msg);
+ ms->settings.plmn_mode = PLMN_MODE_AUTO;
+ gsm322_a_switch_on(ms, msg);
+
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(ms, nmsg);
+
+ return 0;
+}
+
+/* if no cell is found in other states than in *_TRYING_* states */
+static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+
+ /* Tell cell selection process to handle "no cell found". */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ return 0;
+}
+
+/*
+ * cell scanning process
+ */
+
+/* select a suitable and allowable cell */
+static int gsm322_cs_select(struct osmocom_ms *ms, int any)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_sysinfo *s;
+ int i, found = -1, power = 0;
+ uint8_t flags, mask;
+ uint16_t acc_class;
+
+ /* set out access class depending on the cell selection type */
+ if (any) {
+ acc_class = subscr->acc_class | 0x0400; /* add emergency */
+ LOGP(DCS, LOGL_INFO, "Select using access class with Emergency "
+ "class.\n");
+ } else {
+ acc_class = subscr->acc_class;
+ LOGP(DCS, LOGL_INFO, "Select using access class \n");
+ }
+
+ /* flags to match */
+ mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO;
+ 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 */
+
+ /* loop through all scanned frequencies and select cell */
+ for (i = 0; i <= 1023; i++) {
+ cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA;
+ s = cs->list[i].sysinfo;
+
+ /* channel has no informations for us */
+ if (!s || (cs->list[i].flags & mask) != flags) {
+ continue;
+ }
+
+ /* check C1 criteria not fullfilled */
+ // TODO: C1 is also dependant on power class and max power
+ if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) {
+ LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria "
+ "not met. (rxlev=%d < min=%d)\n", i,
+ cs->list[i].rxlev_db, s->rxlev_acc_min_db);
+ continue;
+ }
+
+ /* if cell is barred and we don't override */
+ if (!subscr->acc_barr
+ && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) {
+ LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is "
+ "barred.\n", i);
+ continue;
+ }
+
+ /* if cell is in list of forbidden LAs */
+ if (!subscr->acc_barr
+ && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) {
+ LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in "
+ "list of forbidden LAs. (mcc=%s mnc=%s "
+ "lai=%04x)\n", i, gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac);
+ continue;
+ }
+
+ /* if we have no access to the cell and we don't override */
+ if (!subscr->acc_barr
+ && !(acc_class & (s->class_barr ^ 0xffff))) {
+ LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is "
+ "barred for out access. (access=%04x "
+ "barred=%04x)\n", i, acc_class, s->class_barr);
+ continue;
+ }
+
+ /* store temporary available and allowable flag */
+ cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA;
+
+ /* if we search a specific PLMN, but it does not match */
+ if (!any && cs->mcc && (cs->mcc != s->mcc
+ || cs->mnc != s->mnc)) {
+ LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell "
+ "does not match target PLMN. (mcc=%s "
+ "mnc=%s)\n", i, gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc));
+ continue;
+ }
+
+ LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d "
+ "mcc=%s mnc=%s lac=%04x %s, %s)\n", i,
+ cs->list[i].rxlev_db, gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac, gsm_get_mcc(s->mcc),
+ gsm_get_mnc(s->mcc, s->mnc));
+
+ /* find highest power cell */
+ if (found < 0 || cs->list[i].rxlev_db > power) {
+ power = cs->list[i].rxlev_db;
+ found = i;
+ }
+ }
+
+ if (found >= 0)
+ LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found);
+
+ return found;
+}
+
+/* tune to first/next unscanned frequency and search for PLMN */
+static int gsm322_cs_scan(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int i, j;
+ uint8_t mask, flags;
+ uint32_t weight = 0, test = cs->scan_state;
+
+ /* search for strongest unscanned cell */
+ mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL;
+ 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 */
+ for (i = 0; i <= 1023; i++) {
+ /* skip if band has enough frequencies scanned (3.2.1) */
+ for (j = 0; gsm_sup_smax[j].max; j++) {
+ if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) {
+ if (gsm_sup_smax[j].start >= i
+ && gsm_sup_smax[j].end <= i)
+ break;
+ } else {
+ if (gsm_sup_smax[j].end <= i
+ || gsm_sup_smax[j].start >= i)
+ break;
+ }
+ }
+ if (gsm_sup_smax[j].max) {
+ if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max)
+ continue;
+ }
+ /* search for unscanned frequency */
+ if ((cs->list[i].flags & mask) == flags) {
+ /* weight depends on the power level
+ * if it is the same, it depends on arfcn
+ */
+ test = cs->list[i].rxlev_db + 128;
+ test = (test << 16) | i;
+ if (test >= cs->scan_state)
+ continue;
+ if (test > weight)
+ weight = test;
+ }
+ }
+ cs->scan_state = weight;
+
+ if (!weight)
+ gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs,
+ NULL);
+
+ /* special case for PLMN search */
+ if (cs->state == GSM322_PLMN_SEARCH && !weight) {
+ struct msgb *nmsg;
+
+ /* create AA flag */
+ cs->mcc = cs->mnc = 0;
+ gsm322_cs_select(ms, 0);
+
+ new_c_state(cs, GSM322_C0_NULL);
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END);
+ LOGP(DCS, LOGL_INFO, "PLMN search finished.\n");
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* special case for HPLMN search */
+ if (cs->state == GSM322_HPLMN_SEARCH && !weight) {
+ struct msgb *nmsg;
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
+ LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n");
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
+
+ cs->arfcn = cs->sel_arfcn;
+ LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev "
+ "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db);
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+// start_cs_timer(cs, ms->support.sync_to, 0);
+
+ return 0;
+ }
+
+ /* if all frequencies have been searched */
+ if (!weight) {
+ struct msgb *nmsg;
+#if 0
+ int found, any = 0;
+
+ LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n");
+
+ /* just see, if we search for any cell */
+ if (cs->state == GSM322_C6_ANY_CELL_SEL
+ || cs->state == GSM322_C8_ANY_CELL_RESEL
+ || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
+ any = 1;
+
+ found = gsm322_cs_select(ms, any);
+
+ /* if found */
+ if (found >= 0) {
+ struct gsm322_plmn *plmn = &ms->plmn;
+
+ LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
+ /* tune */
+ cs->arfcn = found;
+ cs->si = cs->list[cs->arfcn].sysinfo;
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+
+ /* selected PLMN (manual) or any PLMN (auto) */
+ switch (ms->settings.plmn_mode) {
+ case PLMN_MODE_AUTO:
+ if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
+ /* PLMN becomes available */
+ nmsg = gsm322_msgb_alloc(
+ GSM322_EVENT_PLMN_AVAIL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ }
+ break;
+ case PLMN_MODE_MANUAL:
+ if (plmn->state == GSM322_M3_NOT_ON_PLMN
+ && gsm322_is_plmn_avail(cs, plmn->mcc,
+ plmn->mnc)) {
+ /* PLMN becomes available */
+ nmsg = gsm322_msgb_alloc(
+ GSM322_EVENT_PLMN_AVAIL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ }
+ break;
+ }
+
+ /* set selected cell */
+ cs->selected = 1;
+ cs->sel_arfcn = cs->arfcn;
+ memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
+ cs->sel_mcc = cs->si->mcc;
+ cs->sel_mnc = cs->si->mnc;
+ cs->sel_lac = cs->si->lac;
+ cs->sel_id = cs->si->cell_id;
+
+ /* tell CS process about available cell */
+ LOGP(DCS, LOGL_INFO, "Cell available.\n");
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
+ } else {
+#endif
+ /* unset selected cell */
+ gsm322_unselect_cell(cs);
+
+ /* tell CS process about no cell available */
+ LOGP(DCS, LOGL_INFO, "No cell available.\n");
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
+// }
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+
+ return 0;
+ }
+
+ /* NOTE: We might already have system information from previous
+ * scan. But we need recent informations, so we scan again!
+ */
+
+ /* Tune to frequency for a while, to receive broadcasts. */
+ cs->arfcn = weight & 1023;
+ LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn,
+ cs->list[cs->arfcn].rxlev_db);
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+// start_cs_timer(cs, ms->support.sync_to, 0);
+
+ /* Allocate/clean system information. */
+ cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
+ if (cs->list[cs->arfcn].sysinfo)
+ memset(cs->list[cs->arfcn].sysinfo, 0,
+ sizeof(struct gsm48_sysinfo));
+ else
+ cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx,
+ struct gsm48_sysinfo);
+ if (!cs->list[cs->arfcn].sysinfo)
+ exit(-ENOMEM);
+ cs->si = cs->list[cs->arfcn].sysinfo;
+
+ /* increase scan counter for each maximum scan range */
+ if (gsm_sup_smax[j].max) {
+ LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n",
+ gsm_sup_smax[j].max - gsm_sup_smax[j].temp,
+ gsm_sup_smax[j].start, gsm_sup_smax[j].end);
+ gsm_sup_smax[j].temp++;
+ }
+
+ return 0;
+}
+
+/* check if cell is now suitable and allowable */
+static int gsm322_cs_store(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct msgb *nmsg;
+ int found, any = 0;
+
+ if (cs->state != GSM322_C2_STORED_CELL_SEL
+ && cs->state != GSM322_C1_NORMAL_CELL_SEL
+ && cs->state != GSM322_C6_ANY_CELL_SEL
+ && cs->state != GSM322_C4_NORMAL_CELL_RESEL
+ && cs->state != GSM322_C8_ANY_CELL_RESEL
+ && cs->state != GSM322_C5_CHOOSE_CELL
+ && cs->state != GSM322_C9_CHOOSE_ANY_CELL
+ && cs->state != GSM322_PLMN_SEARCH
+ && cs->state != GSM322_HPLMN_SEARCH) {
+ LOGP(DCS, LOGL_FATAL, "This must only happen during cell "
+ "(re-)selection, please fix!\n");
+ return -EINVAL;
+ }
+
+ /* store sysinfo */
+ cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO;
+ if (s->cell_barr
+ && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp &&
+ cs->list[cs->arfcn].sysinfo->sp_cbq))
+ cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED;
+ else
+ cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED;
+
+#if 0
+ cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db;
+ cs->list[cs->arfcn].class_barr = s->class_barr;
+ cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch;
+#endif
+
+ /* store selected network */
+ if (s->mcc) {
+#if 0
+ cs->list[cs->arfcn].mcc = s->mcc;
+ cs->list[cs->arfcn].mnc = s->mnc;
+ cs->list[cs->arfcn].lac = s->lac;
+#endif
+
+ if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac))
+ cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD;
+ else
+ cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD;
+ }
+
+ LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d "
+ "mcc=%s mnc=%s lac=%04x)\n", cs->arfcn,
+ cs->list[cs->arfcn].rxlev_db, gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac);
+
+ /* special case for PLMN search */
+ if (cs->state == GSM322_PLMN_SEARCH)
+ /* tune to next cell */
+ return gsm322_cs_scan(ms);
+
+ /* special case for HPLMN search */
+ if (cs->state == GSM322_HPLMN_SEARCH) {
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+
+ if (!gsm322_is_hplmn_avail(cs, subscr->imsi))
+ /* tune to next cell */
+ return gsm322_cs_scan(ms);
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
+ LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n");
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* just see, if we search for any cell */
+ if (cs->state == GSM322_C6_ANY_CELL_SEL
+ || cs->state == GSM322_C8_ANY_CELL_RESEL
+ || cs->state == GSM322_C9_CHOOSE_ANY_CELL)
+ any = 1;
+
+ found = gsm322_cs_select(ms, any);
+
+ /* if not found */
+ if (found < 0) {
+ LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n");
+ /* tune to next cell */
+ return gsm322_cs_scan(ms);
+ }
+
+ LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found);
+ /* tune */
+ cs->arfcn = found;
+ cs->si = cs->list[cs->arfcn].sysinfo;
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+
+ /* selected PLMN (manual) or any PLMN (auto) */
+ switch (ms->settings.plmn_mode) {
+ case PLMN_MODE_AUTO:
+ if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) {
+ /* PLMN becomes available */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ }
+ break;
+ case PLMN_MODE_MANUAL:
+ if (plmn->state == GSM322_M3_NOT_ON_PLMN
+ && gsm322_is_plmn_avail(cs, plmn->mcc,
+ plmn->mnc)) {
+ /* PLMN becomes available */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ }
+ break;
+ }
+
+ /* set selected cell */
+ cs->selected = 1;
+ cs->sel_arfcn = cs->arfcn;
+ memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si));
+ cs->sel_mcc = cs->si->mcc;
+ cs->sel_mnc = cs->si->mnc;
+ cs->sel_lac = cs->si->lac;
+ cs->sel_id = cs->si->cell_id;
+
+ /* tell CS process about available cell */
+ LOGP(DCS, LOGL_INFO, "Cell available.\n");
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+
+ return 0;
+}
+
+/* process system information when returing to idle mode */
+struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct gsm322_ba_list *ba = NULL;
+ int i;
+ uint8_t freq[128];
+
+ /* collect system information received during dedicated mode */
+ if (s->si5
+ && (!s->nb_ext_ind_si5
+ || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis)
+ || (s->si5bis && s->si5ter && s->nb_ext_ind_si5
+ && s->nb_ext_ind_si5bis))) {
+ /* find or create ba list */
+ ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
+ if (!ba) {
+ ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
+ if (!ba)
+ return NULL;
+ ba->mcc = s->mcc;
+ ba->mnc = s->mnc;
+ llist_add_tail(&ba->entry, &cs->ba_list);
+ }
+ /* update (add) ba list */
+ memcpy(freq, ba->freq, sizeof(freq));
+ for (i = 0; i <= 1023; i++) {
+ if ((s->freq[i].mask & FREQ_TYPE_REP))
+ freq[i >> 3] |= (1 << (i & 7));
+ }
+ if (!!memcmp(freq, ba->freq, sizeof(freq))) {
+ LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
+ "%s, %s).\n", gsm_print_mcc(ba->mcc),
+ gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
+ gsm_get_mnc(ba->mcc, ba->mnc));
+ memcpy(ba->freq, freq, sizeof(freq));
+ }
+ }
+
+ return ba;
+}
+
+/* store BA whenever a system informations changes */
+static int gsm322_store_ba_list(struct gsm322_cellsel *cs,
+ struct gsm48_sysinfo *s)
+{
+ struct gsm322_ba_list *ba;
+ int i;
+ uint8_t freq[128];
+
+ /* find or create ba list */
+ ba = gsm322_find_ba_list(cs, s->mcc, s->mnc);
+ if (!ba) {
+ ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
+ if (!ba)
+ return -ENOMEM;
+ ba->mcc = s->mcc;
+ ba->mnc = s->mnc;
+ llist_add_tail(&ba->entry, &cs->ba_list);
+ }
+ /* update ba list */
+ memset(freq, 0, sizeof(freq));
+ freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7));
+ for (i = 0; i <= 1023; i++) {
+ if ((s->freq[i].mask &
+ (FREQ_TYPE_SERV | FREQ_TYPE_NCELL)))
+ freq[i >> 3] |= (1 << (i & 7));
+ }
+ if (!!memcmp(freq, ba->freq, sizeof(freq))) {
+ LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s "
+ "%s, %s).\n", gsm_print_mcc(ba->mcc),
+ gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
+ gsm_get_mnc(ba->mcc, ba->mnc));
+ memcpy(ba->freq, freq, sizeof(freq));
+ }
+
+ return 0;
+}
+
+/* process system information during camping on a cell */
+static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+ struct msgb *nmsg;
+
+ if (rr->state != GSM48_RR_ST_IDLE) {
+ LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n");
+ return -EBUSY;
+ }
+
+ /* Store BA if we have full system info about cells and neigbor cells.
+ * Depending on the extended bit in the channel description,
+ * we require more or less system informations about neighbor cells
+ */
+ if (s->mcc
+ && s->mnc
+ && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
+ || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
+ || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
+ || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
+ && s->si1
+ && s->si2
+ && (!s->nb_ext_ind_si2
+ || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
+ || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
+ && s->nb_ext_ind_si2bis)))
+ gsm322_store_ba_list(cs, s);
+
+ /* update sel_si, if all relevant system informations 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)
+ || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
+ && s->nb_ext_ind_si2bis))) {
+ if (cs->selected) {
+ LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is "
+ "updated.\n");
+ memcpy(&cs->sel_si, s, sizeof(cs->sel_si));
+ //gsm48_sysinfo_dump(s, print_dcs, NULL);
+ }
+ }
+
+ /* check for barred cell */
+ if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) {
+ /* check if cell becomes barred */
+ if (!subscr->acc_barr && s->cell_barr
+ && !(cs->list[cs->arfcn].sysinfo
+ && cs->list[cs->arfcn].sysinfo->sp
+ && cs->list[cs->arfcn].sysinfo->sp_cbq)) {
+ LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n");
+ trigger_resel:
+ /* mark cell as unscanned */
+ cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
+ if (cs->list[cs->arfcn].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
+ cs->arfcn);
+ talloc_free(cs->list[cs->arfcn].sysinfo);
+ cs->list[cs->arfcn].sysinfo = NULL;
+ gsm322_unselect_cell(cs);
+ }
+ /* trigger reselection without queueing,
+ * because other sysinfo message may be queued
+ * before
+ */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+
+ return 0;
+ }
+ /* check if cell access becomes barred */
+ if (!((subscr->acc_class & 0xfbff)
+ & (s->class_barr ^ 0xffff))) {
+ LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n");
+ goto trigger_resel;
+ }
+ }
+
+ /* check if MCC, MNC, LAC, cell ID changes */
+ if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc
+ || cs->sel_lac != s->lac) {
+ LOGP(DCS, LOGL_NOTICE, "Cell changes location area. "
+ "This is not good!\n");
+ goto trigger_resel;
+ }
+ if (cs->sel_id != s->cell_id) {
+ LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. "
+ "This is not good!\n");
+ goto trigger_resel;
+ }
+
+ return 0;
+}
+
+/* process system information during channel scanning */
+static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+
+ /* no sysinfo if we are not done with power scan */
+ if (cs->powerscan) {
+ LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n");
+ return -EINVAL;
+ }
+
+ /* Store BA if we have full system info about cells and neigbor cells.
+ * Depending on the extended bit in the channel description,
+ * we require more or less system informations about neighbor cells
+ */
+ if (s->mcc
+ && s->mnc
+ && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1
+ || gm->sysinfo == GSM48_MT_RR_SYSINFO_2
+ || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis
+ || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter)
+ && s->si1
+ && s->si2
+ && (!s->nb_ext_ind_si2
+ || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis)
+ || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
+ && s->nb_ext_ind_si2bis)))
+ gsm322_store_ba_list(cs, s);
+
+ /* all relevant system informations 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)
+ || (s->si2bis && s->si2ter && s->nb_ext_ind_si2
+ && s->nb_ext_ind_si2bis))) {
+ LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n");
+ /* stop timer */
+ stop_cs_timer(cs);
+
+ //gsm48_sysinfo_dump(s, print_dcs, NULL);
+
+ /* store sysinfo and continue scan */
+ return gsm322_cs_store(ms);
+ }
+
+ /* wait for more sysinfo or timeout */
+ return 0;
+}
+
+static void gsm322_cs_timeout(void *arg)
+{
+ struct gsm322_cellsel *cs = arg;
+ struct osmocom_ms *ms = cs->ms;
+
+ LOGP(DCS, LOGL_INFO, "Cell selection failed.\n");
+
+ /* if we have no lock, we retry */
+ if (cs->ccch_state != GSM322_CCCH_ST_SYNC)
+ LOGP(DCS, LOGL_INFO, "Sync timeout.\n");
+ else
+ LOGP(DCS, LOGL_INFO, "Read timeout.\n");
+
+ LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n",
+ cs->arfcn, cs->list[cs->arfcn].rxlev_db);
+
+ /* remove system information */
+ cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO;
+ if (cs->list[cs->arfcn].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn);
+ talloc_free(cs->list[cs->arfcn].sysinfo);
+ cs->list[cs->arfcn].sysinfo = NULL;
+ gsm322_unselect_cell(cs);
+ }
+
+ /* tune to next cell */
+ gsm322_cs_scan(ms);
+
+ return;
+}
+
+/*
+ * power scan process
+ */
+
+/* search for block of unscanned frequencies and start scanning */
+static int gsm322_cs_powerscan(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int i, s = -1, e;
+ uint8_t mask, flags;
+
+ again:
+
+ /* search for first frequency to scan */
+ mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER;
+ flags = GSM322_CS_FLAG_SUPPORT;
+ if (cs->state == GSM322_C2_STORED_CELL_SEL
+ || cs->state == GSM322_C5_CHOOSE_CELL) {
+ LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n");
+ mask |= GSM322_CS_FLAG_BA;
+ flags |= GSM322_CS_FLAG_BA;
+ } else
+ LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n");
+ for (i = 0; i <= 1023; i++) {
+ if ((cs->list[i].flags & mask) == flags) {
+ s = e = i;
+ break;
+ }
+ }
+
+ /* if there is no more frequency, we can tune to that cell */
+ if (s < 0) {
+ int found = 0;
+
+ /* stop power level scanning */
+ cs->powerscan = 0;
+
+ /* check if not signal is found */
+ for (i = 0; i <= 1023; i++) {
+ if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL))
+ found++;
+ }
+ if (!found) {
+ struct msgb *nmsg;
+
+ LOGP(DCS, LOGL_INFO, "Found no frequency.\n");
+ /* on normal cell selection, start over */
+ if (cs->state == GSM322_C1_NORMAL_CELL_SEL) {
+ for (i = 0; i <= 1023; i++) {
+ /* clear flag that this was scanned */
+ cs->list[i].flags &=
+ ~(GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL
+ | GSM322_CS_FLAG_SYSINFO);
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free "
+ "sysinfo arfcn=%d\n",
+ i);
+ talloc_free(
+ cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ }
+ }
+ /* no cell selected */
+ gsm322_unselect_cell(cs);
+ goto again;
+ }
+ /* on other cell selection, indicate "no cell found" */
+ /* NOTE: PLMN search process handles it.
+ * If not handled there, CS process gets indicated.
+ * If we would continue to process CS, then we might get
+ * our list of scanned cells disturbed.
+ */
+ if (cs->state == GSM322_PLMN_SEARCH)
+ nmsg = gsm322_msgb_alloc(
+ GSM322_EVENT_PLMN_SEARCH_END);
+ else
+ nmsg = gsm322_msgb_alloc(
+ GSM322_EVENT_NO_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ /* if HPLMN search, select last frequency */
+ if (cs->state == GSM322_HPLMN_SEARCH) {
+ new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
+
+ cs->arfcn = cs->sel_arfcn;
+ LOGP(DCS, LOGL_INFO, "Tuning back to frequency "
+ "%d (rxlev %d).\n", cs->arfcn,
+ cs->list[cs->arfcn].rxlev_db);
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+// start_cs_timer(cs, ms->support.sync_to, 0);
+
+ } else
+ new_c_state(cs, GSM322_C0_NULL);
+
+ return 0;
+ }
+ LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found);
+ cs->scan_state = 0xffffffff; /* higher than high */
+ /* clear counter of scanned frequencies of each range */
+ for (i = 0; gsm_sup_smax[i].max; i++)
+ gsm_sup_smax[i].temp = 0;
+ return gsm322_cs_scan(ms);
+ }
+
+ /* search last frequency to scan (en block) */
+ e = i;
+ for (i = s + 1; i <= 1023; i++) {
+ if ((cs->list[i].flags & mask) == flags)
+ e = i;
+ else
+ break;
+ }
+
+ LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e);
+
+ /* start scan on radio interface */
+ cs->powerscan = 1;
+//#warning TESTING!!!!
+//usleep(300000);
+ return l1ctl_tx_pm_req_range(ms, s, e);
+}
+
+static int gsm322_l1_signal(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmocom_ms *ms;
+ struct gsm322_cellsel *cs;
+ struct osmobb_meas_res *mr;
+ struct osmobb_rach_conf *rc;
+ int i;
+ int8_t rxlev_db;
+
+ if (subsys != SS_L1CTL)
+ return 0;
+
+ switch (signal) {
+ case S_L1CTL_PM_RES:
+ mr = signal_data;
+ ms = mr->ms;
+ cs = &ms->cellsel;
+ if (!cs->powerscan)
+ return -EINVAL;
+ i = mr->band_arfcn & 1023;
+ rxlev_db = mr->rx_lev - 110;
+ cs->list[i].rxlev_db = rxlev_db;
+ cs->list[i].flags |= GSM322_CS_FLAG_POWER;
+ if (rxlev_db >= ms->support.min_rxlev_db) {
+ cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL;
+ LOGP(DCS, LOGL_INFO, "Found signal (frequency %d "
+ "rxlev %d)\n", i, cs->list[i].rxlev_db);
+ }
+ break;
+ case S_L1CTL_PM_DONE:
+ LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n");
+ ms = signal_data;
+ cs = &ms->cellsel;
+ if (!cs->powerscan)
+ return -EINVAL;
+ gsm322_cs_powerscan(ms);
+ break;
+ case S_L1CTL_FBSB_RESP:
+ ms = signal_data;
+ cs = &ms->cellsel;
+ if (cs->ccch_state == GSM322_CCCH_ST_INIT) {
+ LOGP(DCS, LOGL_INFO, "Channel synched.\n");
+ cs->ccch_state = GSM322_CCCH_ST_SYNC;
+#if 0
+ stop_cs_timer(cs);
+
+ /* in dedicated mode */
+ if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
+ return gsm48_rr_tx_rand_acc(ms, NULL);
+#endif
+
+ /* set timer for reading BCCH */
+ if (cs->state == GSM322_C2_STORED_CELL_SEL
+ || cs->state == GSM322_C1_NORMAL_CELL_SEL
+ || cs->state == GSM322_C6_ANY_CELL_SEL
+ || cs->state == GSM322_C4_NORMAL_CELL_RESEL
+ || cs->state == GSM322_C8_ANY_CELL_RESEL
+ || cs->state == GSM322_C5_CHOOSE_CELL
+ || cs->state == GSM322_C9_CHOOSE_ANY_CELL
+ || cs->state == GSM322_PLMN_SEARCH
+ || cs->state == GSM322_HPLMN_SEARCH)
+ start_cs_timer(cs, ms->support.scan_to, 0);
+ // TODO: timer depends on BCCH config
+ }
+ break;
+ case S_L1CTL_FBSB_ERR:
+ if (hack) {
+ ms = signal_data;
+ cs = &ms->cellsel;
+ gsm322_sync_to_cell(cs);
+ hack--;
+ LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n");
+ break;
+ }
+ LOGP(DCS, LOGL_INFO, "Channel sync error.\n");
+ ms = signal_data;
+ cs = &ms->cellsel;
+
+ stop_cs_timer(cs);
+ if (cs->selected)
+ gsm322_cs_loss(cs);
+ else
+ gsm322_cs_timeout(cs);
+ break;
+ case S_L1CTL_RACH_CONF:
+ rc = signal_data;
+ ms = rc->ms;
+ gsm48_rr_rach_conf(ms, rc->fn);
+ break;
+ case S_L1CTL_RESET:
+ LOGP(DCS, LOGL_INFO, "Reset\n");
+ break;
+ }
+
+ return 0;
+}
+
+static void gsm322_cs_loss(void *arg)
+{
+ struct gsm322_cellsel *cs = arg;
+ struct osmocom_ms *ms = cs->ms;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n");
+ if (cs->state == GSM322_C3_CAMPED_NORMALLY
+ || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
+ if (rr->state == GSM48_RR_ST_IDLE) {
+ struct msgb *nmsg;
+
+ LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n");
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL);
+ if (!nmsg)
+ return;
+ gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+ } else {
+ LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n");
+ gsm48_rr_los(ms);
+ /* be shure that nothing else is done after here
+ * because the function call above may cause
+ * to return from idle state and trigger cell re-sel.
+ */
+ }
+ }
+
+ return;
+}
+
+/*
+ * handler for cell selection process
+ */
+
+/* start PLMN search */
+static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int i;
+
+ new_c_state(cs, GSM322_PLMN_SEARCH);
+
+ /* mark all frequencies except our own BA to be scanned */
+ for (i = 0; i <= 1023; i++) {
+ cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL
+ | GSM322_CS_FLAG_SYSINFO);
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
+ talloc_free(cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ gsm322_unselect_cell(cs);
+ }
+ }
+
+ /* unset selected cell */
+ gsm322_unselect_cell(cs);
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start HPLMN search */
+static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int i;
+
+ new_c_state(cs, GSM322_HPLMN_SEARCH);
+
+ /* mark all frequencies except our own BA to be scanned */
+ for (i = 0; i <= 1023; i++) {
+ if (i != cs->sel_arfcn
+ && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)
+ && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) {
+ cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL
+ | GSM322_CS_FLAG_SYSINFO);
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
+ i);
+ talloc_free(cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ }
+ }
+ }
+
+ /* no cell selected */
+ gsm322_unselect_cell(cs);
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start stored cell selection */
+static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int i;
+
+ new_c_state(cs, GSM322_C2_STORED_CELL_SEL);
+
+ /* flag all frequencies that are in current band allocation */
+ for (i = 0; i <= 1023; i++) {
+ if ((ba->freq[i >> 3] & (1 << (i & 7))))
+ cs->list[i].flags |= GSM322_CS_FLAG_BA;
+ else
+ cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
+ }
+
+ /* unset selected cell */
+ gsm322_unselect_cell(cs);
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start noraml cell selection */
+static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ int i;
+
+ /* except for stored cell selection state, we weed to rescan ?? */
+ if (cs->state != GSM322_C2_STORED_CELL_SEL) {
+ for (i = 0; i <= 1023; i++) {
+ cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL
+ | GSM322_CS_FLAG_SYSINFO);
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
+ i);
+ talloc_free(cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ }
+ }
+ }
+
+ new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL);
+
+ /* unset selected cell */
+ gsm322_unselect_cell(cs);
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start any cell selection */
+static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ /* in case we already tried any cell (re-)selection, power scan again */
+ if (cs->state == GSM322_C0_NULL
+ || cs->state == GSM322_C6_ANY_CELL_SEL
+ || cs->state == GSM322_C8_ANY_CELL_RESEL) {
+ int i;
+
+ for (i = 0; i <= 1023; i++) {
+ cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL
+ | GSM322_CS_FLAG_SYSINFO);
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n",
+ i);
+ talloc_free(cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ }
+ }
+ }
+ /* after re-selection, indicate no cell found */
+ if (cs->state == GSM322_C6_ANY_CELL_SEL
+ || cs->state == GSM322_C8_ANY_CELL_RESEL) {
+ struct msgb *nmsg;
+
+ /* tell that we have no cell found */
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(ms, nmsg);
+
+ } else {
+ new_c_state(cs, GSM322_C6_ANY_CELL_SEL);
+ }
+
+ cs->mcc = cs->mnc = 0;
+
+ /* unset selected cell */
+ gsm322_unselect_cell(cs);
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start noraml cell re-selection */
+static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL);
+
+ /* NOTE: We keep our scan info we have so far.
+ * This may cause a skip in power scan. */
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start any cell re-selection */
+static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ new_c_state(cs, GSM322_C8_ANY_CELL_RESEL);
+
+ /* NOTE: We keep our scan info we have so far.
+ * This may cause a skip in power scan. */
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* create temporary ba range with given frequency ranges */
+struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms,
+ uint32_t *range, uint8_t ranges)
+{
+ static struct gsm322_ba_list ba;
+ uint16_t lower, higher;
+
+ memset(&ba, 0, sizeof(ba));
+
+ while(ranges--) {
+ lower = *range & 1023;
+ higher = (*range >> 16) & 1023;
+ range++;
+ LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher);
+ /* GSM 05.08 6.3 */
+ while (1) {
+ ba.freq[lower >> 3] |= 1 << (lower & 7);
+ if (lower == higher)
+ break;
+ lower = (lower + 1) & 1023;
+ }
+ }
+
+ return &ba;
+}
+
+/* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */
+static int gsm322_cs_choose(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_ba_list *ba = NULL;
+ int i;
+
+ /* NOTE: The call to this function is synchron to RR layer, so
+ * we may access the BA range there.
+ */
+ if (rr->ba_ranges)
+ ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges);
+ else {
+ LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n");
+ /* get and update BA of last received sysinfo 5* */
+ ba = gsm322_cs_sysinfo_sacch(ms);
+ if (!ba) {
+ LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored "
+ "BA list.\n");
+ ba = gsm322_find_ba_list(cs, cs->sel_si.mcc,
+ cs->sel_si.mnc);
+ }
+ }
+
+ if (!ba) {
+ struct msgb *nmsg;
+
+ LOGP(DCS, LOGL_INFO, "No BA list to use.\n");
+
+ /* tell CS to start over */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+
+ return 0;
+ }
+
+ /* flag all frequencies that are in current band allocation */
+ for (i = 0; i <= 1023; i++) {
+ if (cs->state == GSM322_C5_CHOOSE_CELL) {
+ if ((ba->freq[i >> 3] & (1 << (i & 7)))) {
+ cs->list[i].flags |= GSM322_CS_FLAG_BA;
+ } else {
+ cs->list[i].flags &= ~GSM322_CS_FLAG_BA;
+ }
+ }
+ cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER
+ | GSM322_CS_FLAG_SIGNAL
+ | GSM322_CS_FLAG_SYSINFO);
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
+ talloc_free(cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ }
+ }
+
+ /* unset selected cell */
+ gsm322_unselect_cell(cs);
+
+ /* start power scan */
+ return gsm322_cs_powerscan(ms);
+}
+
+/* start 'Choose cell' after returning to idle mode */
+static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ new_c_state(cs, GSM322_C5_CHOOSE_CELL);
+
+ return gsm322_cs_choose(ms);
+}
+
+/* start 'Choose any cell' after returning to idle mode */
+static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL);
+
+ return gsm322_cs_choose(ms);
+}
+
+/* a new PLMN is selected by PLMN search process */
+static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_ba_list *ba;
+
+ cs->mcc = plmn->mcc;
+ cs->mnc = plmn->mnc;
+
+ LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s "
+ "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc),
+ gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc),
+ gsm_get_mnc(cs->mcc, cs->mnc));
+
+ /* search for BA list */
+ ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc);
+
+ if (ba) {
+ LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n");
+ return gsm322_c_stored_cell_sel(ms, ba);
+ } else {
+ LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n");
+ return gsm322_c_normal_cell_sel(ms, msg);
+ }
+}
+
+/* a suitable cell was found, so we camp normally */
+static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct msgb *nmsg;
+
+ LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s "
+ "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
+ gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
+ gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
+
+ /* tell that we have selected a (new) cell */
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(ms, nmsg);
+
+ new_c_state(cs, GSM322_C3_CAMPED_NORMALLY);
+
+ return 0;
+}
+
+/* a not suitable cell was found, so we camp on any cell */
+static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct msgb *nmsg;
+
+ LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s "
+ "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc),
+ gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc),
+ gsm_get_mnc(cs->sel_mcc, cs->sel_mnc));
+
+
+ /* tell that we have selected a (new) cell */
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(ms, nmsg);
+
+ new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL);
+
+ return 0;
+}
+
+/* go connected mode */
+static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ /* check for error */
+ if (!cs->selected)
+ return -EINVAL;
+ cs->arfcn = cs->sel_arfcn;
+
+ /* be sure to go to current camping frequency on return */
+ LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+ cs->si = cs->list[cs->arfcn].sysinfo;
+//#warning TESTING!!!!
+//usleep(300000);
+
+ return 0;
+}
+
+static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ /* check for error */
+ if (!cs->selected)
+ return -EINVAL;
+ cs->arfcn = cs->sel_arfcn;
+
+ /* be sure to go to current camping frequency on return */
+ LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn);
+ hack = 5;
+ gsm322_sync_to_cell(cs);
+ cs->si = cs->list[cs->arfcn].sysinfo;
+
+ return 0;
+}
+
+/* switch on */
+static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ /* if no SIM is is MS */
+ if (!subscr->sim_valid) {
+ LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n");
+ return gsm322_c_any_cell_sel(ms, msg);
+ LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n");
+ }
+
+ /* stay in NULL state until PLMN is selected */
+
+ return 0;
+}
+
+/*
+ * state machines
+ */
+
+/* state machine for automatic PLMN selection events */
+static struct plmnastatelist {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} plmnastatelist[] = {
+ {SBIT(GSM322_A0_NULL),
+ GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on},
+
+ {SBIT(GSM322_A0_NULL),
+ GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn},
+
+ {ALL_STATES,
+ GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off},
+
+ {SBIT(GSM322_A6_NO_SIM),
+ GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on},
+
+ {ALL_STATES,
+ GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert},
+
+ {ALL_STATES,
+ GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed},
+
+ {ALL_STATES,
+ GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed},
+
+ {SBIT(GSM322_A1_TRYING_RPLMN),
+ GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn},
+
+ {SBIT(GSM322_A1_TRYING_RPLMN),
+ GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn},
+
+ {SBIT(GSM322_A1_TRYING_RPLMN),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn},
+
+ {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN),
+ GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected},
+
+ {SBIT(GSM322_A2_ON_PLMN),
+ GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na},
+
+ {SBIT(GSM322_A2_ON_PLMN),
+ GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start},
+
+ {SBIT(GSM322_A2_ON_PLMN),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio},
+
+ {SBIT(GSM322_A2_ON_PLMN),
+ GSM322_EVENT_USER_RESEL, gsm322_a_user_resel},
+
+ {SBIT(GSM322_A3_TRYING_PLMN),
+ GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn},
+
+ {SBIT(GSM322_A3_TRYING_PLMN),
+ GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn},
+
+ {SBIT(GSM322_A3_TRYING_PLMN),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn},
+
+ {SBIT(GSM322_A5_HPLMN_SEARCH),
+ GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn},
+
+ {SBIT(GSM322_A5_HPLMN_SEARCH),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn},
+
+ {SBIT(GSM322_A4_WAIT_FOR_PLMN),
+ GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail},
+
+ {ALL_STATES,
+ GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual},
+
+ {ALL_STATES,
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
+};
+
+#define PLMNASLLEN \
+ (sizeof(plmnastatelist) / sizeof(struct plmnastatelist))
+
+static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+ int msg_type = gm->msg_type;
+ int rc;
+ int i;
+
+ LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN "
+ "selection in state '%s'\n", ms->name, get_event_name(msg_type),
+ plmn_a_state_names[plmn->state]);
+ /* find function for current state and message */
+ for (i = 0; i < PLMNASLLEN; i++)
+ if ((msg_type == plmnastatelist[i].type)
+ && ((1 << plmn->state) & plmnastatelist[i].states))
+ break;
+ if (i == PLMNASLLEN) {
+ LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = plmnastatelist[i].rout(ms, msg);
+
+ return rc;
+}
+
+/* state machine for manual PLMN selection events */
+static struct plmnmstatelist {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} plmnmstatelist[] = {
+ {SBIT(GSM322_M0_NULL),
+ GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on},
+
+ {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) |
+ SBIT(GSM322_M2_ON_PLMN),
+ GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns},
+
+ {ALL_STATES,
+ GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off},
+
+ {SBIT(GSM322_M5_NO_SIM),
+ GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on},
+
+ {ALL_STATES,
+ GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert},
+
+ {ALL_STATES,
+ GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed},
+
+ {SBIT(GSM322_M1_TRYING_RPLMN),
+ GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
+
+ {SBIT(GSM322_M1_TRYING_RPLMN),
+ GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
+
+ {SBIT(GSM322_M1_TRYING_RPLMN),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
+
+ {SBIT(GSM322_M1_TRYING_RPLMN),
+ GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected},
+
+ {SBIT(GSM322_M2_ON_PLMN),
+ GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
+
+ {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) |
+ SBIT(GSM322_M4_TRYING_PLMN),
+ GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed},
+
+ {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN),
+ GSM322_EVENT_USER_RESEL, gsm322_m_user_resel},
+
+ {SBIT(GSM322_M3_NOT_ON_PLMN),
+ GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail},
+
+ {SBIT(GSM322_M3_NOT_ON_PLMN),
+ GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn},
+
+ {SBIT(GSM322_M4_TRYING_PLMN),
+ GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn},
+
+ {SBIT(GSM322_M4_TRYING_PLMN),
+ GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns},
+
+ {SBIT(GSM322_M4_TRYING_PLMN),
+ GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns},
+
+ {SBIT(GSM322_M4_TRYING_PLMN),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns},
+
+ {ALL_STATES,
+ GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto},
+
+ {ALL_STATES,
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found},
+};
+
+#define PLMNMSLLEN \
+ (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist))
+
+static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+ int msg_type = gm->msg_type;
+ int rc;
+ int i;
+
+ LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection "
+ "in state '%s'\n", ms->name, get_event_name(msg_type),
+ plmn_m_state_names[plmn->state]);
+ /* find function for current state and message */
+ for (i = 0; i < PLMNMSLLEN; i++)
+ if ((msg_type == plmnmstatelist[i].type)
+ && ((1 << plmn->state) & plmnmstatelist[i].states))
+ break;
+ if (i == PLMNMSLLEN) {
+ LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = plmnmstatelist[i].rout(ms, msg);
+
+ return rc;
+}
+
+/* dequeue GSM 03.22 PLMN events */
+int gsm322_plmn_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&plmn->event_queue))) {
+ /* send event to PLMN select process */
+ if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
+ gsm322_a_event(ms, msg);
+ else
+ gsm322_m_event(ms, msg);
+ msgb_free(msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/* state machine for channel selection events */
+static struct cellselstatelist {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} cellselstatelist[] = {
+ {ALL_STATES,
+ GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on},
+
+ {ALL_STATES,
+ GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel},
+
+ {ALL_STATES,
+ GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn},
+
+ {ALL_STATES,
+ GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search},
+
+ {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
+ SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL),
+ GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally},
+
+ {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
+ SBIT(GSM322_C8_ANY_CELL_RESEL),
+ GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell},
+
+ {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) |
+ SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
+ SBIT(GSM322_C0_NULL),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel},
+
+ {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
+ SBIT(GSM322_C4_NORMAL_CELL_RESEL),
+ GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel},
+
+ {SBIT(GSM322_C3_CAMPED_NORMALLY),
+ GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1},
+
+ {SBIT(GSM322_C7_CAMPED_ANY_CELL),
+ GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2},
+
+ {SBIT(GSM322_C3_CAMPED_NORMALLY),
+ GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell},
+
+ {SBIT(GSM322_C7_CAMPED_ANY_CELL),
+ GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell},
+
+ {SBIT(GSM322_C3_CAMPED_NORMALLY),
+ GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel},
+
+ {SBIT(GSM322_C7_CAMPED_ANY_CELL),
+ GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel},
+
+ {SBIT(GSM322_C7_CAMPED_ANY_CELL),
+ GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel},
+
+ {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) |
+ SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) |
+ SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) |
+ SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH),
+ GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch},
+
+ {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL),
+ GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch},
+
+ {SBIT(GSM322_C3_CAMPED_NORMALLY),
+ GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search},
+};
+
+#define CELLSELSLLEN \
+ (sizeof(cellselstatelist) / sizeof(struct cellselstatelist))
+
+int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm322_msg *gm = (struct gsm322_msg *) msg->data;
+ int msg_type = gm->msg_type;
+ int rc;
+ int i;
+
+ LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state "
+ "'%s'\n", ms->name, get_event_name(msg_type),
+ cs_state_names[cs->state]);
+ /* find function for current state and message */
+ for (i = 0; i < CELLSELSLLEN; i++)
+ if ((msg_type == cellselstatelist[i].type)
+ && ((1 << cs->state) & cellselstatelist[i].states))
+ break;
+ if (i == CELLSELSLLEN) {
+ LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = cellselstatelist[i].rout(ms, msg);
+
+ return rc;
+}
+
+/* dequeue GSM 03.22 cell selection events */
+int gsm322_cs_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&cs->event_queue))) {
+ /* send event to cell selection process */
+ gsm322_c_event(ms, msg);
+ msgb_free(msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/*
+ * dump lists
+ */
+
+int gsm322_dump_sorted_plmn(struct osmocom_ms *ms)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_plmn_list *temp;
+
+ printf("MCC |MNC |allowed|rx-lev\n");
+ printf("-------+-------+-------+-------\n");
+ llist_for_each_entry(temp, &plmn->sorted_plmn, entry) {
+ printf("%s |%s%s |%s |%d\n", gsm_print_mcc(temp->mcc),
+ gsm_print_mnc(temp->mnc),
+ ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
+ (temp->cause) ? "no ":"yes", temp->rxlev_db);
+ }
+
+ return 0;
+}
+
+int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ int i;
+ struct gsm48_sysinfo *s;
+
+ print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|"
+ "prio |min-db |max-pwr\n");
+ print(priv, "-------+-------+-------+-------+-------+-------+-------+"
+ "-------+-------+-------\n");
+ for (i = 0; i <= 1023; i++) {
+ s = cs->list[i].sysinfo;
+ if (!s || !(cs->list[i].flags & flags))
+ continue;
+ print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db);
+ if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) {
+ print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc),
+ ((s->mnc & 0x00f) == 0x00f) ? " ":"");
+ print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id);
+ if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD))
+ print(priv, "yes |");
+ else
+ print(priv, "no |");
+ if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED))
+ print(priv, "barred |");
+ else {
+ if (cs->list[i].sysinfo->cell_barr)
+ print(priv, "low |");
+ else
+ print(priv, "normal |");
+ }
+ print(priv, "%4d |%4d\n", s->rxlev_acc_min_db,
+ s->ms_txpwr_max_ccch);
+ } else
+ print(priv, "n/a |n/a |n/a |n/a |n/a |"
+ "n/a |n/a |n/a\n");
+ }
+ print(priv, "\n");
+
+ return 0;
+}
+
+int gsm322_dump_forbidden_la(struct osmocom_ms *ms,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_la_list *temp;
+
+ print(priv, "MCC |MNC |LAC |cause\n");
+ print(priv, "-------+-------+-------+-------\n");
+ llist_for_each_entry(temp, &plmn->forbidden_la, entry)
+ print(priv, "%s |%s%s |0x%04x |#%d\n",
+ gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
+ ((temp->mnc & 0x00f) == 0x00f) ? " ":"",
+ temp->lac, temp->cause);
+
+ return 0;
+}
+
+int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ struct gsm322_ba_list *ba;
+ int i;
+
+ llist_for_each_entry(ba, &cs->ba_list, entry) {
+ if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc))
+ continue;
+ print(priv, "Band Allocation of network: MCC %03d MNC %02d "
+ "(%s, %s)\n", ba->mcc, ba->mnc, gsm_get_mcc(ba->mcc),
+ gsm_get_mnc(ba->mcc, ba->mnc));
+ for (i = 0; i <= 1023; i++) {
+ if ((ba->freq[i >> 3] & (1 << (i & 7))))
+ print(priv, " %d", i);
+ }
+ print(priv, "\n");
+ }
+
+ return 0;
+}
+
+/*
+ * initialization
+ */
+
+int gsm322_init(struct osmocom_ms *ms)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ OSMOCOM_FILE *fp;
+ char suffix[] = ".ba";
+ char filename[sizeof(ms->name) + strlen(suffix) + 1];
+ int i;
+ struct gsm322_ba_list *ba;
+ uint8_t buf[4];
+
+ LOGP(DPLMN, LOGL_INFO, "init PLMN process\n");
+ LOGP(DCS, LOGL_INFO, "init Cell Selection process\n");
+
+ memset(plmn, 0, sizeof(*plmn));
+ memset(cs, 0, sizeof(*cs));
+ plmn->ms = ms;
+ cs->ms = ms;
+
+ /* set initial state */
+ plmn->state = 0;
+ cs->state = 0;
+ ms->settings.plmn_mode = PLMN_MODE_AUTO;
+
+ /* init lists */
+ INIT_LLIST_HEAD(&plmn->event_queue);
+ INIT_LLIST_HEAD(&cs->event_queue);
+ INIT_LLIST_HEAD(&plmn->sorted_plmn);
+ INIT_LLIST_HEAD(&plmn->forbidden_la);
+ INIT_LLIST_HEAD(&cs->ba_list);
+
+ /* set supported frequencies in cell selection list */
+ for (i = 0; i <= 1023; i++)
+ if ((ms->support.freq_map[i >> 3] & (1 << (i & 7))))
+ cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT;
+
+ /* read BA list */
+ strcpy(filename, ms->name);
+ strcat(filename, suffix);
+ fp = osmocom_fopen(filename, "r");
+ if (fp) {
+ int rc;
+
+ while(!osmocom_feof(fp)) {
+ ba = talloc_zero(l23_ctx, struct gsm322_ba_list);
+ if (!ba)
+ return -ENOMEM;
+ rc = osmocom_fread(buf, 4, 1, fp);
+ if (!rc) {
+ talloc_free(ba);
+ break;
+ }
+ ba->mcc = (buf[0] << 8) | buf[1];
+ ba->mnc = (buf[2] << 8) | buf[3];
+ rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp);
+ if (!rc) {
+ talloc_free(ba);
+ break;
+ }
+ llist_add_tail(&ba->entry, &cs->ba_list);
+ LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s "
+ "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc),
+ gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc),
+ gsm_get_mnc(ba->mcc, ba->mnc));
+ }
+ osmocom_fclose(fp);
+ } else
+ LOGP(DCS, LOGL_INFO, "No stored BA list\n");
+
+ register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
+
+ return 0;
+}
+
+int gsm322_exit(struct osmocom_ms *ms)
+{
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct llist_head *lh, *lh2;
+ struct msgb *msg;
+ OSMOCOM_FILE *fp;
+ char suffix[] = ".ba";
+ char filename[sizeof(ms->name) + strlen(suffix) + 1];
+ struct gsm322_ba_list *ba;
+ uint8_t buf[4];
+ int i;
+
+ LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n");
+ LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n");
+
+ unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL);
+
+ /* stop cell selection process (if any) */
+ new_c_state(cs, GSM322_C0_NULL);
+
+ /* stop timers */
+ stop_cs_timer(cs);
+ stop_plmn_timer(plmn);
+
+ /* flush sysinfo */
+ for (i = 0; i <= 1023; i++) {
+ if (cs->list[i].sysinfo) {
+ LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i);
+ talloc_free(cs->list[i].sysinfo);
+ cs->list[i].sysinfo = NULL;
+ }
+ cs->list[i].flags = 0;
+ }
+
+ /* store BA list */
+ strcpy(filename, ms->name);
+ strcat(filename, suffix);
+ fp = osmocom_fopen(filename, "w");
+ if (fp) {
+ int rc;
+
+ llist_for_each_entry(ba, &cs->ba_list, entry) {
+ buf[0] = ba->mcc >> 8;
+ buf[1] = ba->mcc & 0xff;
+ buf[2] = ba->mnc >> 8;
+ buf[3] = ba->mnc & 0xff;
+ rc = osmocom_fwrite(buf, 4, 1, fp);
+ rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp);
+ 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));
+ }
+ osmocom_fclose(fp);
+ } else
+ LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n");
+
+ /* free lists */
+ while ((msg = msgb_dequeue(&plmn->event_queue)))
+ msgb_free(msg);
+ while ((msg = msgb_dequeue(&cs->event_queue)))
+ msgb_free(msg);
+ llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) {
+ llist_del(lh);
+ talloc_free(lh);
+ }
+ llist_for_each_safe(lh, lh2, &plmn->forbidden_la) {
+ llist_del(lh);
+ talloc_free(lh);
+ }
+ llist_for_each_safe(lh, lh2, &cs->ba_list) {
+ llist_del(lh);
+ talloc_free(lh);
+ }
+ return 0;
+}
+
+
diff --git a/src/host/layer23/src/gsm48_cc.c b/src/host/layer23/src/gsm48_cc.c
new file mode 100644
index 00000000..386fe80b
--- /dev/null
+++ b/src/host/layer23/src/gsm48_cc.c
@@ -0,0 +1,2124 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/utils.h>
+#include <osmocore/gsm48.h>
+#include <osmocore/talloc.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/mncc.h>
+#include <osmocom/transaction.h>
+#include <osmocom/gsm48_cc.h>
+
+extern void *l23_ctx;
+
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
+static int gsm48_rel_null_free(struct gsm_trans *trans);
+int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans,
+ u_int32_t callref, int location, int value);
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
+static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg);
+
+/*
+ * init
+ */
+
+int gsm48_cc_init(struct osmocom_ms *ms)
+{
+ struct gsm48_cclayer *cc = &ms->cclayer;
+
+ cc->ms = ms;
+
+ if (!cc->mncc_upqueue.next == 0)
+ return 0;
+
+ LOGP(DCC, LOGL_INFO, "init Call Control\n");
+
+ INIT_LLIST_HEAD(&cc->mncc_upqueue);
+
+ return 0;
+}
+
+int gsm48_cc_exit(struct osmocom_ms *ms)
+{
+ struct gsm48_cclayer *cc = &ms->cclayer;
+ struct gsm_trans *trans, *trans2;
+ struct msgb *msg;
+
+ LOGP(DCC, LOGL_INFO, "exit Call Control processes for %s\n", ms->name);
+
+ llist_for_each_entry_safe(trans, trans2, &ms->trans_list, entry) {
+ if (trans->protocol == GSM48_PDISC_CC)
+ LOGP(DCC, LOGL_NOTICE, "Free pendig CC-transaction.\n");
+ trans_free(trans);
+ }
+
+ while ((msg = msgb_dequeue(&cc->mncc_upqueue)))
+ msgb_free(msg);
+
+ return 0;
+}
+
+/*
+ * messages
+ */
+
+/* names of MNCC-SAP */
+static const struct value_string gsm_mncc_names[] = {
+ { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" },
+ { MNCC_SETUP_IND, "MNCC_SETUP_IND" },
+ { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" },
+ { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" },
+ { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" },
+ { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" },
+ { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" },
+ { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" },
+ { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" },
+ { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" },
+ { MNCC_ALERT_IND, "MNCC_ALERT_IND" },
+ { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" },
+ { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" },
+ { MNCC_DISC_REQ, "MNCC_DISC_REQ" },
+ { MNCC_DISC_IND, "MNCC_DISC_IND" },
+ { MNCC_REL_REQ, "MNCC_REL_REQ" },
+ { MNCC_REL_IND, "MNCC_REL_IND" },
+ { MNCC_REL_CNF, "MNCC_REL_CNF" },
+ { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" },
+ { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" },
+ { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" },
+ { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" },
+ { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" },
+ { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" },
+ { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" },
+ { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" },
+ { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" },
+ { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" },
+ { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" },
+ { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" },
+ { MNCC_HOLD_IND, "MNCC_HOLD_IND" },
+ { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" },
+ { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" },
+ { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" },
+ { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" },
+ { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" },
+ { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" },
+ { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" },
+ { MNCC_REJ_REQ, "MNCC_REJ_REQ" },
+ { MNCC_REJ_IND, "MNCC_REJ_IND" },
+ { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" },
+ { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" },
+ { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" },
+ { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" },
+ { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" },
+ { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " },
+ { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" },
+ { 0, NULL }
+};
+
+const char *get_mncc_name(int value)
+{
+ return get_value_string(gsm_mncc_names, value);
+}
+
+/* push MMCC header and send to MM */
+static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans,
+ int msg_type)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_mmxx_hdr *mmh;
+ int emergency = 0;
+
+ /* Add protocol type and transaction ID */
+ gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
+
+ /* indicate emergency setup to MM layer */
+ if (gh->msg_type == GSM48_MT_CC_EMERG_SETUP)
+ emergency = 1;
+
+ /* push RR header */
+ msgb_push(msg, sizeof(struct gsm48_mmxx_hdr));
+ mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ mmh->msg_type = msg_type;
+ mmh->ref = trans->callref;
+ mmh->transaction_id = trans->transaction_id;
+ mmh->emergency = emergency;
+
+ /* send message to MM */
+ LOGP(DCC, LOGL_INFO, "Sending '%s' using %s (callref=%x, "
+ "transaction_id=%d)\n", gsm48_cc_msg_name(gh->msg_type),
+ get_mmxx_name(msg_type), trans->callref, trans->transaction_id);
+ return gsm48_mmxx_downmsg(trans->ms, msg);
+}
+
+/* enqueue message to application (MNCC-SAP) */
+static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans,
+ int msg_type, struct gsm_mncc *mncc)
+{
+ struct gsm48_cclayer *cc = &ms->cclayer;
+ struct msgb *msg;
+
+ if (trans)
+ LOGP(DCC, LOGL_INFO, "(ms %s ti %x) Sending '%s' to MNCC.\n",
+ ms->name, trans->transaction_id,
+ get_mncc_name(msg_type));
+ else
+ LOGP(DCC, LOGL_INFO, "(ms %s ti -) Sending '%s' to MNCC.\n",
+ ms->name, get_mncc_name(msg_type));
+
+ mncc->msg_type = msg_type;
+
+ msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC");
+ if (!msg)
+ return -ENOMEM;
+ memcpy(msg->data, mncc, sizeof(struct gsm_mncc));
+ msgb_enqueue(&cc->mncc_upqueue, msg);
+
+ return 0;
+}
+
+/* dequeue messages to layer 4 */
+int mncc_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm48_cclayer *cc = &ms->cclayer;
+ struct gsm_mncc *mncc;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&cc->mncc_upqueue))) {
+ mncc = (struct gsm_mncc *)msg->data;
+ if (cc->mncc_recv)
+ cc->mncc_recv(ms, mncc->msg_type, mncc);
+ work = 1; /* work done */
+ talloc_free(msg);
+ }
+
+ return work;
+}
+
+
+/*
+ * state transition
+ */
+
+static void new_cc_state(struct gsm_trans *trans, int state)
+{
+ if (state > 31 || state < 0)
+ return;
+
+ DEBUGP(DCC, "new state %s -> %s\n",
+ gsm48_cc_state_name(trans->cc.state),
+ gsm48_cc_state_name(state));
+
+ trans->cc.state = state;
+}
+
+/*
+ * timers
+ */
+
+/* timeout events of all timers */
+static void gsm48_cc_timeout(void *arg)
+{
+ struct gsm_trans *trans = arg;
+ int disconnect = 0, release = 0, abort = 1;
+ int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER;
+ int mo_location = GSM48_CAUSE_LOC_PRN_S_LU;
+ int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
+ int l4_location = GSM48_CAUSE_LOC_PRN_S_LU;
+ struct gsm_mncc mo_rel, l4_rel;
+
+ memset(&mo_rel, 0, sizeof(struct gsm_mncc));
+ mo_rel.callref = trans->callref;
+ memset(&l4_rel, 0, sizeof(struct gsm_mncc));
+ l4_rel.callref = trans->callref;
+
+ LOGP(DCC, LOGL_INFO, "Timer T%x has fired.\n", trans->cc.Tcurrent);
+
+ switch(trans->cc.Tcurrent) {
+ case 0x303:
+ /* abort if connection is not already esablished */
+ if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND)
+ abort = 1;
+ else
+ release = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x305:
+ release = 1;
+ mo_cause = trans->cc.msg.cause.value;
+ mo_location = trans->cc.msg.cause.location;
+ break;
+ case 0x308:
+ if (!trans->cc.T308_second) {
+ /* restart T308 a second time */
+ gsm48_cc_tx_release(trans, &trans->cc.msg);
+ trans->cc.T308_second = 1;
+ break; /* stay in release state */
+ }
+ /* release MM conn, got NULL state, free trans */
+ gsm48_rel_null_free(trans);
+
+ return;
+ case 0x310:
+ disconnect = 1;
+ l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
+ break;
+ case 0x313:
+ disconnect = 1;
+ /* unknown, did not find it in the specs */
+ break;
+ default:
+ release = 1;
+ }
+
+ if ((release || abort) && trans->callref) {
+ /* process release towards layer 4 */
+ mncc_release_ind(trans->ms, trans, trans->callref,
+ l4_location, l4_cause);
+ }
+
+ if (disconnect && trans->callref) {
+ /* process disconnect towards layer 4 */
+ mncc_set_cause(&l4_rel, l4_location, l4_cause);
+ mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &l4_rel);
+ }
+
+ /* process disconnect towards mobile station */
+ if (disconnect || release || abort) {
+ mncc_set_cause(&mo_rel, mo_location, mo_cause);
+ mo_rel.cause.diag[0] =
+ ((trans->cc.Tcurrent & 0xf00) >> 8) + '0';
+ mo_rel.cause.diag[1] =
+ ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0';
+ mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0';
+ mo_rel.cause.diag_len = 3;
+
+ if (disconnect)
+ gsm48_cc_tx_disconnect(trans, &mo_rel);
+ if (release)
+ gsm48_cc_tx_release(trans, &mo_rel);
+ if (abort) {
+ /* release MM conn, got NULL state, free trans */
+ gsm48_rel_null_free(trans);
+ }
+ }
+}
+
+/* start various timers */
+static void gsm48_start_cc_timer(struct gsm_trans *trans, int current,
+ int sec, int micro)
+{
+ LOGP(DCC, LOGL_INFO, "starting timer T%x with %d seconds\n", current,
+ sec);
+ trans->cc.timer.cb = gsm48_cc_timeout;
+ trans->cc.timer.data = trans;
+ bsc_schedule_timer(&trans->cc.timer, sec, micro);
+ trans->cc.Tcurrent = current;
+}
+
+/* stop various timers */
+static void gsm48_stop_cc_timer(struct gsm_trans *trans)
+{
+ if (bsc_timer_pending(&trans->cc.timer)) {
+ LOGP(DCC, LOGL_INFO, "stopping pending timer T%x\n",
+ trans->cc.Tcurrent);
+ bsc_del_timer(&trans->cc.timer);
+ trans->cc.Tcurrent = 0;
+ }
+}
+
+/*
+ * process handlers (misc)
+ */
+
+/* Call Control Specific transaction release.
+ * gets called by trans_free, DO NOT CALL YOURSELF!
+ */
+void _gsm48_cc_trans_free(struct gsm_trans *trans)
+{
+ gsm48_stop_cc_timer(trans);
+
+ /* send release to L4, if callref still exists */
+ if (trans->callref) {
+ /* Ressource unavailable */
+ mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ }
+ if (trans->cc.state != GSM_CSTATE_NULL)
+ new_cc_state(trans, GSM_CSTATE_NULL);
+}
+
+/* release MM connection, go NULL state, free transaction */
+static int gsm48_rel_null_free(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+
+ /* release MM connection */
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref,
+ trans->transaction_id);
+ if (!nmsg)
+ return -ENOMEM;
+ LOGP(DCC, LOGL_INFO, "Sending MMCC_REL_REQ\n");
+ gsm48_mmxx_downmsg(trans->ms, nmsg);
+
+ new_cc_state(trans, GSM_CSTATE_NULL);
+
+ trans->callref = 0;
+ trans_free(trans);
+
+ return 0;
+}
+
+void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
+{
+ data->fields |= MNCC_F_CAUSE;
+ data->cause.location = loc;
+ data->cause.value = val;
+}
+
+/* send release indication to upper layer */
+int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans,
+ u_int32_t callref, int location, int value)
+{
+ struct gsm_mncc rel;
+
+ memset(&rel, 0, sizeof(rel));
+ rel.callref = callref;
+ mncc_set_cause(&rel, location, value);
+ return mncc_recvmsg(ms, trans, MNCC_REL_IND, &rel);
+}
+
+/* sending status message in response to unknown message */
+static int gsm48_cc_tx_status(struct gsm_trans *trans, int cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ uint8_t *cause_ie, *call_state_ie;
+
+ LOGP(DCC, LOGL_INFO, "sending STATUS (cause %d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_STATUS;
+
+ cause_ie = msgb_put(nmsg, 3);
+ cause_ie[0] = 2;
+ cause_ie[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_PRN_S_LU;
+ cause_ie[2] = 0x80 | cause;
+
+ call_state_ie = msgb_put(nmsg, 1);
+ call_state_ie[0] = 0xc0 | trans->cc.state;
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* reply status enquiry */
+static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
+{
+ LOGP(DCC, LOGL_INFO, "received STATUS ENQUIREY\n");
+
+ return gsm48_cc_tx_status(trans, GSM48_CC_CAUSE_RESP_STATUS_INQ);
+}
+
+/*
+ * process handlers (mobile originating call establish)
+ */
+
+/* on SETUP request from L4, init MM connection */
+static int gsm48_cc_init_mm(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+ struct gsm_mncc *data = arg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ /* store setup message */
+ memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
+
+ new_cc_state(trans, GSM_CSTATE_MM_CONNECTION_PEND);
+
+ /* establish MM connection */
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_REQ, trans->callref,
+ trans->transaction_id);
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *) nmsg->data;
+ if (data->emergency)
+ nmmh->emergency = 1;
+ LOGP(DCC, LOGL_INFO, "Sending MMCC_EST_REQ\n");
+ return gsm48_mmxx_downmsg(trans->ms, nmsg);
+}
+
+/* abort connection prior SETUP */
+static int gsm48_cc_abort_mm(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+
+ /* abort MM connection */
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref,
+ trans->transaction_id);
+ if (!nmsg)
+ return -ENOMEM;
+ LOGP(DCC, LOGL_INFO, "Sending MMCC_REL_REQ\n");
+ gsm48_mmxx_downmsg(trans->ms, nmsg);
+
+ new_cc_state(trans, GSM_CSTATE_NULL);
+
+ trans->callref = 0;
+ trans_free(trans);
+
+ return 0;
+}
+
+/* setup message from upper layer */
+static int gsm48_cc_tx_setup(struct gsm_trans *trans)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm_mncc *setup = &trans->cc.msg;
+ int rc, transaction_id;
+ uint8_t *ie;
+
+ LOGP(DCC, LOGL_INFO, "sending SETUP\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ /* transaction id must not be assigned */
+ if (trans->transaction_id != 0xff) { /* unasssigned */
+ LOGP(DCC, LOGL_NOTICE, "TX Setup with assigned transaction. "
+ "This is not allowed!\n");
+ /* Temporarily out of order */
+ rc = mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_NORMAL_UNSPEC);
+ trans->callref = 0;
+ trans_free(trans);
+ return rc;
+ }
+
+ /* Get free transaction_id */
+ transaction_id = trans_assign_trans_id(trans->ms, GSM48_PDISC_CC, 0);
+ if (transaction_id < 0) {
+ /* no free transaction ID */
+ rc = mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ trans->callref = 0;
+ trans_free(trans);
+ return rc;
+ }
+ trans->transaction_id = transaction_id;
+
+ gh->msg_type = (setup->emergency) ? GSM48_MT_CC_EMERG_SETUP :
+ GSM48_MT_CC_SETUP;
+
+ /* actually we have to start it when CM SERVICE REQUEST has been sent,
+ * but there is no primitive for that defined. i think it is ok to
+ * do it here rather than inventing MMCC-NOTIFY-IND.
+ */
+ gsm48_start_cc_timer(trans, 0x303, GSM48_T303_MS);
+
+ if (!setup->emergency) {
+ /* bearer capability */
+ gsm48_encode_bearer_cap(nmsg, 0, &setup->bearer_cap);
+ /* facility */
+ if (setup->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &setup->facility);
+ /* called party BCD number */
+ if (setup->fields & MNCC_F_CALLED)
+ gsm48_encode_called(nmsg, &setup->called);
+ /* user-user */
+ if (setup->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &setup->useruser);
+ /* ss version */
+ if (setup->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &setup->ssversion);
+ /* CLIR suppression */
+ if (setup->clir.sup) {
+ ie = msgb_put(nmsg, 1);
+ ie[0] = GSM48_IE_CLIR_SUPP;
+ }
+ /* CLIR invocation */
+ if (setup->clir.inv) {
+ ie = msgb_put(nmsg, 1);
+ ie[0] = GSM48_IE_CLIR_INVOC;
+ }
+ /* cc cap */
+ if (setup->fields & MNCC_F_CCCAP)
+ gsm48_encode_cccap(nmsg, &setup->cccap);
+ }
+
+ /* actually MM CONNECTION PENDING */
+ new_cc_state(trans, GSM_CSTATE_INITIATED);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* progress is received from lower layer */
+static int gsm48_cc_rx_progress(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc progress;
+
+ LOGP(DCC, LOGL_INFO, "received PROGRESS\n");
+
+ memset(&progress, 0, sizeof(struct gsm_mncc));
+ progress.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_PROGR_IND, 0);
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ progress.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&progress.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ /* store last progress indicator */
+ trans->cc.prog_ind = progress.progress.descr;
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ progress.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&progress.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_PROGRESS_IND, &progress);
+}
+
+/* call proceeding is received from lower layer */
+static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans,
+ struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc call_proc;
+
+ LOGP(DCC, LOGL_INFO, "sending CALL PROCEEDING\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&call_proc, 0, sizeof(struct gsm_mncc));
+ call_proc.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+#if 0
+ /* repeat */
+ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
+ call_conf.repeat = 1;
+ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ))
+ call_conf.repeat = 2;
+#endif
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ call_proc.fields |= MNCC_F_BEARER_CAP;
+ gsm48_decode_bearer_cap(&call_proc.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ call_proc.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&call_proc.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ call_proc.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&call_proc.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ /* store last progress indicator */
+ trans->cc.prog_ind = call_proc.progress.descr;
+ }
+
+ /* start T310, if last progress indicator was 1 or 2 or 64 */
+ if (trans->cc.prog_ind == 1
+ || trans->cc.prog_ind == 2
+ || trans->cc.prog_ind == 64)
+ gsm48_start_cc_timer(trans, 0x310, GSM48_T310_MS);
+
+ new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_CALL_PROC_IND,
+ &call_proc);
+}
+
+/* alerting is received by the lower layer */
+static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc alerting;
+
+ LOGP(DCC, LOGL_INFO, "sending ALERTING\n");
+
+ gsm48_stop_cc_timer(trans);
+ /* no T301 in MS call control */
+
+ memset(&alerting, 0, sizeof(struct gsm_mncc));
+ alerting.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ alerting.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&alerting.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ alerting.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&alerting.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ alerting.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&alerting.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_ALERT_IND,
+ &alerting);
+}
+
+/* connect is received from lower layer */
+static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc connect;
+
+ LOGP(DCC, LOGL_INFO, "received CONNECT\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&connect, 0, sizeof(struct gsm_mncc));
+ connect.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ connect.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&connect.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* connected */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CONN_BCD)) {
+ connect.fields |= MNCC_F_CONNECTED;
+ gsm48_decode_connected(&connect.connected,
+ TLVP_VAL(&tp, GSM48_IE_CONN_BCD)-1);
+ }
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ connect.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&connect.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ connect.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&connect.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ /* ACTIVE state is set during this: */
+ gsm48_cc_tx_connect_ack(trans, NULL);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_CNF, &connect);
+}
+
+/* connect ack message from upper layer */
+static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending CONNECT ACKNOWLEDGE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_CONNECT_ACK;
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/*
+ * process handlers (mobile terminating call establish)
+ */
+
+/* setup is received from lower layer */
+static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc setup;
+
+ LOGP(DCC, LOGL_INFO, "sending SETUP\n");
+
+ memset(&setup, 0, sizeof(struct gsm_mncc));
+ setup.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ setup.fields |= MNCC_F_BEARER_CAP;
+ gsm48_decode_bearer_cap(&setup.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ setup.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&setup.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ setup.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&setup.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* signal */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SIGNAL)) {
+ setup.fields |= MNCC_F_SIGNAL;
+ gsm48_decode_signal(&setup.signal,
+ TLVP_VAL(&tp, GSM48_IE_SIGNAL)-1);
+ }
+ /* calling party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CALLING_BCD)) {
+ setup.fields |= MNCC_F_CALLING;
+ gsm48_decode_calling(&setup.calling,
+ TLVP_VAL(&tp, GSM48_IE_CALLING_BCD)-1);
+ }
+ /* called party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
+ setup.fields |= MNCC_F_CALLED;
+ gsm48_decode_called(&setup.called,
+ TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
+ }
+ /* redirecting party bcd number */
+ if (TLVP_PRESENT(&tp, GSM48_IE_REDIR_BCD)) {
+ setup.fields |= MNCC_F_REDIRECTING;
+ gsm48_decode_redirecting(&setup.redirecting,
+ TLVP_VAL(&tp, GSM48_IE_REDIR_BCD)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ setup.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&setup.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
+
+ /* indicate setup to MNCC */
+ mncc_recvmsg(trans->ms, trans, MNCC_SETUP_IND, &setup);
+
+ return 0;
+}
+
+/* call conf message from upper layer */
+static int gsm48_cc_tx_call_conf(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *confirm = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending CALL CONFIRMED (proceeding)\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_CALL_CONF;
+
+ new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
+
+ /* bearer capability */
+ if (confirm->fields & MNCC_F_BEARER_CAP)
+ gsm48_encode_bearer_cap(nmsg, 0, &confirm->bearer_cap);
+ /* cause */
+ if (confirm->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 0, &confirm->cause);
+ /* cc cap */
+ if (confirm->fields & MNCC_F_CCCAP)
+ gsm48_encode_cccap(nmsg, &confirm->cccap);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* alerting message from upper layer */
+static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *alerting = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending ALERTING\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_ALERTING;
+
+ /* facility */
+ if (alerting->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &alerting->facility);
+ /* user-user */
+ if (alerting->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &alerting->useruser);
+ /* ss version */
+ if (alerting->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &alerting->ssversion);
+
+ new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* connect message from upper layer */
+static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *connect = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending CONNECT\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_CONNECT;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x313, GSM48_T313_MS);
+
+ /* facility */
+ if (connect->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &connect->facility);
+ /* user-user */
+ if (connect->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &connect->useruser);
+ /* ss version */
+ if (connect->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &connect->ssversion);
+
+ new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* connect ack is received from lower layer */
+static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc connect_ack;
+
+ LOGP(DCC, LOGL_INFO, "received CONNECT ACKNOWLEDGE\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ memset(&connect_ack, 0, sizeof(struct gsm_mncc));
+ connect_ack.callref = trans->callref;
+ return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_COMPL_IND,
+ &connect_ack);
+}
+
+/*
+ * process handlers (during active state)
+ */
+
+/* notify message from upper layer */
+static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *notify = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending NOTIFY\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_NOTIFY;
+
+ /* notify */
+ gsm48_encode_notify(nmsg, notify->notify);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* notify is received from lower layer */
+static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc notify;
+
+ LOGP(DCC, LOGL_INFO, "received NOTIFY\n");
+
+ memset(&notify, 0, sizeof(struct gsm_mncc));
+ notify.callref = trans->callref;
+ /* notify */
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of notify message error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_notify(&notify.notify, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_NOTIFY_IND, &notify);
+}
+
+/* start dtmf message from upper layer */
+static int gsm48_cc_tx_start_dtmf(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *dtmf = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending START DTMF\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_START_DTMF;
+
+ /* keypad */
+ gsm48_encode_keypad(nmsg, dtmf->keypad);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* start dtmf ack is received from lower layer */
+static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc dtmf;
+
+ LOGP(DCC, LOGL_INFO, "received START DTMF ACKNOWLEDGE\n");
+
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* keypad facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) {
+ dtmf.fields |= MNCC_F_KEYPAD;
+ gsm48_decode_keypad(&dtmf.keypad,
+ TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
+ }
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_RSP, &dtmf);
+}
+
+/* start dtmf rej is received from lower layer */
+static int gsm48_cc_rx_start_dtmf_rej(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc dtmf;
+
+ LOGP(DCC, LOGL_INFO, "received START DTMF REJECT\n");
+
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+ /* cause */
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of dtmf reject message "
+ "error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_cause(&dtmf.cause, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_REJ, &dtmf);
+}
+
+/* stop dtmf message from upper layer */
+static int gsm48_cc_tx_stop_dtmf(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending STOP DTMF\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_STOP_DTMF;
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* stop dtmf ack is received from lower layer */
+static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc dtmf;
+
+ LOGP(DCC, LOGL_INFO, "received STOP DTMF ACKNOWLEDGE\n");
+
+ memset(&dtmf, 0, sizeof(struct gsm_mncc));
+ dtmf.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf);
+}
+
+/* hold message from upper layer */
+static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending HOLD\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_HOLD;
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* hold ack is received from lower layer */
+static int gsm48_cc_rx_hold_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc hold;
+
+ LOGP(DCC, LOGL_INFO, "received HOLD ACKNOWLEDGE\n");
+
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = trans->callref;
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_CNF, &hold);
+}
+
+/* hold rej is received from lower layer */
+static int gsm48_cc_rx_hold_rej(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc hold;
+
+ LOGP(DCC, LOGL_INFO, "received HOLD REJECT\n");
+
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = trans->callref;
+ /* cause */
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of hold reject message "
+ "error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_cause(&hold.cause, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_REJ, &hold);
+}
+
+/* retrieve message from upper layer */
+static int gsm48_cc_tx_retrieve(struct gsm_trans *trans, void *arg)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending RETRIEVE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_RETR;
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* retrieve ack is received from lower layer */
+static int gsm48_cc_rx_retrieve_ack(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm_mncc retrieve;
+
+ LOGP(DCC, LOGL_INFO, "received RETRIEVE ACKNOWLEDGE\n");
+
+ memset(&retrieve, 0, sizeof(struct gsm_mncc));
+ retrieve.callref = trans->callref;
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_CNF, &retrieve);
+}
+
+/* retrieve rej is received from lower layer */
+static int gsm48_cc_rx_retrieve_rej(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc retrieve;
+
+ LOGP(DCC, LOGL_INFO, "received RETRIEVE REJECT\n");
+
+ memset(&retrieve, 0, sizeof(struct gsm_mncc));
+ retrieve.callref = trans->callref;
+ /* cause */
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of retrieve reject message "
+ "error.\n");
+ return -EINVAL;
+ }
+ gsm48_decode_cause(&retrieve.cause, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_REJ, &retrieve);
+}
+
+/* facility message from upper layer or from timer event */
+static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *fac = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending FACILITY\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_FACILITY;
+
+ /* facility */
+ gsm48_encode_facility(nmsg, 1, &fac->facility);
+ /* ss version */
+ if (fac->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &fac->ssversion);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* facility is received from lower layer */
+static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc fac;
+
+ LOGP(DCC, LOGL_INFO, "received FACILITY\n");
+
+ memset(&fac, 0, sizeof(struct gsm_mncc));
+ fac.callref = trans->callref;
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of facility message "
+ "error.\n");
+ return -EINVAL;
+ }
+ /* facility */
+ gsm48_decode_facility(&fac.facility, gh->data);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_FACILITY_IND, &fac);
+}
+
+/* user info message from upper layer or from timer event */
+static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *user = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending USERINFO\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_USER_INFO;
+
+ /* user-user */
+ if (user->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 1, &user->useruser);
+ /* more data */
+ if (user->more)
+ gsm48_encode_more(nmsg);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* user info is received from lower layer */
+static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc user;
+
+ LOGP(DCC, LOGL_INFO, "received USERINFO\n");
+
+ memset(&user, 0, sizeof(struct gsm_mncc));
+ user.callref = trans->callref;
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of userinfo message "
+ "error.\n");
+ return -EINVAL;
+ }
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_USER_USER, 0);
+ /* user-user */
+ gsm48_decode_useruser(&user.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ /* more data */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA))
+ user.more = 1;
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_USERINFO_IND, &user);
+}
+
+/* modify message from upper layer or from timer event */
+static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *modify = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending MODIFY\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_MODIFY;
+
+ gsm48_start_cc_timer(trans, 0x323, GSM48_T323_MS);
+
+ /* bearer capability */
+ gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
+
+ new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* modify complete is received from lower layer */
+static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans,
+ struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc modify;
+
+ LOGP(DCC, LOGL_INFO, "received MODIFY COMPLETE\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of modify complete message "
+ "error.\n");
+ return -EINVAL;
+ }
+ /* bearer capability */
+ gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_CNF, &modify);
+}
+
+/* modify reject is received from lower layer */
+static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc modify;
+
+ LOGP(DCC, LOGL_INFO, "received MODIFY REJECT\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of modify reject message "
+ "error.\n");
+ return -EINVAL;
+ }
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
+ /* bearer capability */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
+ modify.fields |= MNCC_F_BEARER_CAP;
+ gsm48_decode_bearer_cap(&modify.bearer_cap,
+ TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
+ }
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ modify.fields |= MNCC_F_CAUSE;
+ gsm48_decode_cause(&modify.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_REJ, &modify);
+}
+
+/* modify is received from lower layer */
+static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_mncc modify;
+
+ LOGP(DCC, LOGL_INFO, "received MODIFY\n");
+
+ memset(&modify, 0, sizeof(struct gsm_mncc));
+ modify.callref = trans->callref;
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of modify message error.\n");
+ return -EINVAL;
+ }
+ /* bearer capability */
+ gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data);
+
+ new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_IND, &modify);
+}
+
+/* modify complete message from upper layer or from timer event */
+static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *modify = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending MODIFY COMPLETE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
+
+ /* bearer capability */
+ gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* modify reject message from upper layer or from timer event */
+static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *modify = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending MODIFY REJECT\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
+
+ /* bearer capability */
+ gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap);
+ /* cause */
+ gsm48_encode_cause(nmsg, 1, &modify->cause);
+
+ new_cc_state(trans, GSM_CSTATE_ACTIVE);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/*
+ * process handlers (call clearing)
+ */
+
+static struct gsm_mncc_cause default_cause = {
+ .location = GSM48_CAUSE_LOC_PRN_S_LU,
+ .coding = 0,
+ .rec = 0,
+ .rec_val = 0,
+ .value = GSM48_CC_CAUSE_NORMAL_UNSPEC,
+ .diag_len = 0,
+ .diag = { 0 },
+};
+
+/* disconnect message from upper layer or from timer event */
+static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *disc = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending DISCONNECT\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_DISCONNECT;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS);
+
+ /* cause */
+ if (disc->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 1, &disc->cause);
+ else
+ gsm48_encode_cause(nmsg, 1, &default_cause);
+
+ /* facility */
+ if (disc->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &disc->facility);
+ /* progress */
+ if (disc->fields & MNCC_F_PROGRESS)
+ gsm48_encode_progress(nmsg, 0, &disc->progress);
+ /* user-user */
+ if (disc->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &disc->useruser);
+ /* ss version */
+ if (disc->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &disc->ssversion);
+
+ new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ);
+
+ return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+}
+
+/* release message from upper layer or from timer event */
+static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *rel = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending RELEASE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_RELEASE;
+
+ gsm48_stop_cc_timer(trans);
+ gsm48_start_cc_timer(trans, 0x308, GSM48_T308_MS);
+
+ /* cause */
+ if (rel->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 0, &rel->cause);
+ /* facility */
+ if (rel->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &rel->facility);
+ /* user-user */
+ if (rel->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &rel->useruser);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &rel->ssversion);
+
+ trans->cc.T308_second = 0;
+ memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc));
+
+ if (trans->cc.state != GSM_CSTATE_RELEASE_REQ)
+ new_cc_state(trans, GSM_CSTATE_RELEASE_REQ);
+
+ gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+
+ /* release without sending MMCC_REL_REQ */
+ new_cc_state(trans, GSM_CSTATE_NULL);
+ trans->callref = 0;
+ trans_free(trans);
+
+ return 0;
+}
+
+/* reject message from upper layer */
+static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg)
+{
+ struct gsm_mncc *rel = arg;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+
+ LOGP(DCC, LOGL_INFO, "sending RELEASE COMPLETE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
+
+ gsm48_stop_cc_timer(trans);
+
+ /* cause */
+ if (rel->fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 0, &rel->cause);
+ /* facility */
+ if (rel->fields & MNCC_F_FACILITY)
+ gsm48_encode_facility(nmsg, 0, &rel->facility);
+ /* user-user */
+ if (rel->fields & MNCC_F_USERUSER)
+ gsm48_encode_useruser(nmsg, 0, &rel->useruser);
+ /* ss version */
+ if (rel->fields & MNCC_F_SSVERSION)
+ gsm48_encode_ssversion(nmsg, &rel->ssversion);
+
+ /* release without sending MMCC_REL_REQ */
+ new_cc_state(trans, GSM_CSTATE_NULL);
+ trans->callref = 0;
+ trans_free(trans);
+
+ return 0;
+}
+
+/* disconnect is received from lower layer */
+static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc disc;
+
+ LOGP(DCC, LOGL_INFO, "received DISCONNECT\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND);
+
+ memset(&disc, 0, sizeof(struct gsm_mncc));
+ disc.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_CAUSE, 0);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ disc.fields |= MNCC_F_CAUSE;
+ gsm48_decode_cause(&disc.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ disc.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&disc.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* progress */
+ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
+ disc.fields |= MNCC_F_PROGRESS;
+ gsm48_decode_progress(&disc.progress,
+ TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ disc.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&disc.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ /* store disconnect cause for T305 expiry */
+ memcpy(&trans->cc.msg, &disc, sizeof(struct gsm_mncc));
+
+ return mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &disc);
+}
+
+/* release is received from lower layer */
+static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc rel;
+
+ LOGP(DCC, LOGL_INFO, "received RELEASE\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ rel.fields |= MNCC_F_CAUSE;
+ gsm48_decode_cause(&rel.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ rel.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&rel.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ rel.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&rel.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
+ /* release collision 5.4.5 */
+ mncc_recvmsg(trans->ms, trans, MNCC_REL_CNF, &rel);
+ } else {
+ struct msgb *nmsg;
+
+ /* forward cause only */
+ LOGP(DCC, LOGL_INFO, "sending RELEASE COMPLETE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
+
+ if (rel.fields & MNCC_F_CAUSE)
+ gsm48_encode_cause(nmsg, 0, &rel.cause);
+
+ gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ);
+
+ /* release indication */
+ mncc_recvmsg(trans->ms, trans, MNCC_REL_IND, &rel);
+ }
+
+ /* release MM conn, got NULL state, free trans */
+ return gsm48_rel_null_free(trans);
+}
+
+/* release complete is received from lower layer */
+static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct gsm_mncc rel;
+
+ LOGP(DCC, LOGL_INFO, "received RELEASE COMPLETE\n");
+
+ gsm48_stop_cc_timer(trans);
+
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = trans->callref;
+ tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
+ /* cause */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
+ rel.fields |= MNCC_F_CAUSE;
+ gsm48_decode_cause(&rel.cause,
+ TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
+ }
+ /* facility */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
+ rel.fields |= MNCC_F_FACILITY;
+ gsm48_decode_facility(&rel.facility,
+ TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
+ }
+ /* user-user */
+ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
+ rel.fields |= MNCC_F_USERUSER;
+ gsm48_decode_useruser(&rel.useruser,
+ TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
+ }
+
+ if (trans->callref) {
+ switch (trans->cc.state) {
+ case GSM_CSTATE_CALL_PRESENT:
+ mncc_recvmsg(trans->ms, trans,
+ MNCC_REJ_IND, &rel);
+ break;
+ case GSM_CSTATE_RELEASE_REQ:
+ mncc_recvmsg(trans->ms, trans,
+ MNCC_REL_CNF, &rel);
+ break;
+ default:
+ mncc_recvmsg(trans->ms, trans,
+ MNCC_REL_IND, &rel);
+ }
+ }
+
+ /* release MM conn, got NULL state, free trans */
+ return gsm48_rel_null_free(trans);
+}
+
+/*
+ * state machines
+ */
+
+/* state trasitions for MNCC messages (upper layer) */
+static struct downstate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct gsm_trans *trans, void *arg);
+} downstatelist[] = {
+ /* mobile originating call establishment */
+ {SBIT(GSM_CSTATE_NULL), /* 5.2.1 */
+ MNCC_SETUP_REQ, gsm48_cc_init_mm},
+
+ {SBIT(GSM_CSTATE_MM_CONNECTION_PEND), /* 5.2.1 */
+ MNCC_REL_REQ, gsm48_cc_abort_mm},
+
+ /* mobile terminating call establishment */
+ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.1 */
+ MNCC_CALL_CONF_REQ, gsm48_cc_tx_call_conf},
+
+ {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* 5.2.2.3.2 */
+ MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
+
+ {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) |
+ SBIT(GSM_CSTATE_CALL_RECEIVED), /* 5.2.2.5 */
+ MNCC_SETUP_RSP, gsm48_cc_tx_connect},
+
+ /* signalling during call */
+ {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */
+ MNCC_NOTIFY_REQ, gsm48_cc_tx_notify},
+
+ {ALL_STATES, /* 5.5.7.1 */
+ MNCC_START_DTMF_REQ, gsm48_cc_tx_start_dtmf},
+
+ {ALL_STATES, /* 5.5.7.3 */
+ MNCC_STOP_DTMF_REQ, gsm48_cc_tx_stop_dtmf},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_HOLD_REQ, gsm48_cc_tx_hold},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_RETRIEVE_REQ, gsm48_cc_tx_retrieve},
+
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ),
+ MNCC_FACILITY_REQ, gsm48_cc_tx_facility},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo},
+
+ /* clearing */
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) -
+ SBIT(GSM_CSTATE_RELEASE_REQ) -
+ SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.3.1 */
+ MNCC_DISC_REQ, gsm48_cc_tx_disconnect},
+
+ {SBIT(GSM_CSTATE_INITIATED),
+ MNCC_REJ_REQ, gsm48_cc_tx_release_compl},
+
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) -
+ SBIT(GSM_CSTATE_RELEASE_REQ), /* ??? */
+ MNCC_REL_REQ, gsm48_cc_tx_release},
+
+ /* modify */
+ {SBIT(GSM_CSTATE_ACTIVE),
+ MNCC_MODIFY_REQ, gsm48_cc_tx_modify},
+
+ {SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
+ MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete},
+
+ {SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
+ MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject},
+};
+
+#define DOWNSLLEN \
+ (sizeof(downstatelist) / sizeof(struct downstate))
+
+int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg)
+{
+ struct gsm_mncc *data = arg;
+ struct gsm_trans *trans;
+ int i, rc;
+
+ /* Find callref */
+ trans = trans_find_by_callref(ms, data->callref);
+
+ if (!trans) {
+ /* check for SETUP message */
+ if (msg_type != MNCC_SETUP_REQ) {
+ /* Invalid call reference */
+ return mncc_release_ind(ms, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID);
+ }
+ if (data->callref >= 0x40000000) {
+ LOGP(DCC, LOGL_FATAL, "MNCC ref wrong.\n");
+ return mncc_release_ind(ms, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID);
+ }
+
+ /* Create transaction */
+ trans = trans_alloc(ms, GSM48_PDISC_CC, 0xff, data->callref);
+ if (!trans) {
+ /* No memory or whatever */
+ return mncc_release_ind(ms, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+ }
+ }
+
+ /* Find function for current state and message */
+ for (i = 0; i < DOWNSLLEN; i++)
+ if ((msg_type == downstatelist[i].type)
+ && ((1 << trans->cc.state) & downstatelist[i].states))
+ break;
+ if (i == DOWNSLLEN) {
+ LOGP(DCC, LOGL_NOTICE, "Message unhandled at this "
+ "state.\n");
+ return 0;
+ }
+
+ rc = downstatelist[i].rout(trans, arg);
+
+ return rc;
+}
+
+/* state trasitions for call control messages (lower layer) */
+static struct datastate {
+ u_int32_t states;
+ int type;
+ int (*rout) (struct gsm_trans *trans, struct msgb *msg);
+} datastatelist[] = {
+ /* mobile originating call establishment */
+ {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.3 */
+ GSM48_MT_CC_CALL_PROC, gsm48_cc_rx_call_proceeding},
+
+ {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) |
+ SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.4.1 */
+ MNCC_PROGRESS_REQ, gsm48_cc_rx_progress},
+
+ {SBIT(GSM_CSTATE_INITIATED) |
+ SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.5 */
+ GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting},
+
+ {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) |
+ SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.6 */
+ GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect},
+
+ /* mobile terminating call establishment */
+ {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */
+ GSM48_MT_CC_SETUP, gsm48_cc_rx_setup},
+
+ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.6 */
+ GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack},
+
+ /* signalling during call */
+ {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */
+ GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify},
+
+ {ALL_STATES, /* 8.4 */
+ GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq},
+
+ {ALL_STATES, /* 5.5.7.2 */
+ GSM48_MT_CC_START_DTMF_ACK, gsm48_cc_rx_start_dtmf_ack},
+
+ {ALL_STATES, /* 5.5.7.2 */
+ GSM48_MT_CC_START_DTMF_REJ, gsm48_cc_rx_start_dtmf_rej},
+
+ {ALL_STATES, /* 5.5.7.4 */
+ GSM48_MT_CC_STOP_DTMF_ACK, gsm48_cc_rx_stop_dtmf_ack},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_HOLD_ACK, gsm48_cc_rx_hold_ack},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_HOLD_REJ, gsm48_cc_rx_hold_rej},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_RETR_ACK, gsm48_cc_rx_retrieve_ack},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_RETR_REJ, gsm48_cc_rx_retrieve_rej},
+
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL),
+ GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility},
+
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo},
+
+ /* clearing */
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ) -
+ SBIT(GSM_CSTATE_DISCONNECT_IND), /* 5.4.4.1.1 */
+ GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect},
+
+ {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.3.3 & 5.4.5!!!*/
+ GSM48_MT_CC_RELEASE, gsm48_cc_rx_release},
+
+ {ALL_STATES, /* 5.4.4.1.3 */
+ GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl},
+
+ /* modify */
+ {SBIT(GSM_CSTATE_ACTIVE),
+ GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify},
+
+ {SBIT(GSM_CSTATE_MO_TERM_MODIFY),
+ GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete},
+
+ {SBIT(GSM_CSTATE_MO_TERM_MODIFY),
+ GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject},
+};
+
+#define DATASLLEN \
+ (sizeof(datastatelist) / sizeof(struct datastate))
+
+static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg)
+{
+ struct osmocom_ms *ms = trans->ms;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ int msg_type = gh->msg_type & 0xbf;
+ uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
+ /* flip */
+ int msg_supported = 0; /* determine, if message is supported at all */
+ int i, rc;
+
+ /* set transaction ID, if not already */
+ trans->transaction_id = transaction_id;
+
+ /* pull the MMCC header */
+ msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
+
+ LOGP(DCC, LOGL_INFO, "(ms %s) Received '%s' in CC state %s\n", ms->name,
+ gsm48_cc_msg_name(msg_type),
+ gsm48_cc_state_name(trans->cc.state));
+
+ /* find function for current state and message */
+ for (i = 0; i < DATASLLEN; i++) {
+ if (msg_type == datastatelist[i].type)
+ msg_supported = 1;
+ if ((msg_type == datastatelist[i].type)
+ && ((1 << trans->cc.state) & datastatelist[i].states))
+ break;
+ }
+ if (i == DATASLLEN) {
+ if (msg_supported) {
+ LOGP(DCC, LOGL_NOTICE, "Message unhandled at this "
+ "state.\n");
+ return gsm48_cc_tx_status(trans,
+ GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE);
+ } else {
+ LOGP(DCC, LOGL_NOTICE, "Message not supported.\n");
+ return gsm48_cc_tx_status(trans,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
+ }
+ }
+
+ rc = datastatelist[i].rout(trans, msg);
+
+ return rc;
+}
+
+/* receive message from MM layer */
+int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ int msg_type = mmh->msg_type;
+ struct gsm_trans *trans;
+ int rc = 0;
+
+ trans = trans_find_by_callref(ms, mmh->ref);
+ if (!trans) {
+ trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id,
+ mmh->ref);
+ if (!trans)
+ return -ENOMEM;
+ }
+
+ LOGP(DCC, LOGL_INFO, "(ms %s) Received '%s' in CC state %s\n", ms->name,
+ get_mmxx_name(msg_type),
+ gsm48_cc_state_name(trans->cc.state));
+
+ switch (msg_type) {
+ case GSM48_MMCC_EST_IND:
+ /* data included */
+ rc = gsm48_cc_data_ind(trans, msg);
+ break;
+ case GSM48_MMCC_EST_CNF:
+ /* send setup after confirm */
+ if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND)
+ rc = gsm48_cc_tx_setup(trans);
+ else
+ LOGP(DCC, LOGL_ERROR, "Oops, MMCC-EST-CONF in state "
+ "%d?\n", trans->cc.state);
+ break;
+ case GSM48_MMCC_ERR_IND: /* no supporting re-establishment */
+ case GSM48_MMCC_REL_IND:
+ /* release L4, release transaction */
+ mncc_release_ind(trans->ms, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU, mmh->cause);
+ /* release without sending MMCC_REL_REQ */
+ new_cc_state(trans, GSM_CSTATE_NULL);
+ trans->callref = 0;
+ trans_free(trans);
+ break;
+ case GSM48_MMCC_DATA_IND:
+ rc = gsm48_cc_data_ind(trans, msg);
+ break;
+ case GSM48_MMCC_UNIT_DATA_IND:
+ break;
+ case GSM48_MMCC_SYNC_IND:
+ break;
+ default:
+ LOGP(DCC, LOGL_NOTICE, "Message unhandled.\n");
+ rc = -ENOTSUP;
+ }
+
+ return rc;
+}
+
diff --git a/src/host/layer23/src/gsm48_mm.c b/src/host/layer23/src/gsm48_mm.c
new file mode 100644
index 00000000..4c00bbb7
--- /dev/null
+++ b/src/host/layer23/src/gsm48_mm.c
@@ -0,0 +1,4100 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/utils.h>
+#include <osmocore/gsm48.h>
+#include <osmocore/talloc.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/gsm48_cc.h>
+#include <osmocom/l23_app.h>
+#include <osmocom/networks.h>
+
+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);
+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);
+static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms);
+static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg);
+static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate);
+static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg);
+
+/*
+ * notes
+ */
+
+/*
+ * Notes on IMSI detach procedure:
+ *
+ * At the end of the procedure, the state of MM, RR, cell selection: No SIM.
+ *
+ * In MM IDLE state, cell available: RR is establised, IMSI detach specific
+ * procedure is performed.
+ *
+ * In MM IDLE state, no cell: State is silently changed to No SIM.
+ *
+ * During any MM connection state, or Wait for network command: All MM
+ * connections (if any) are released locally, and IMSI detach specific
+ * procedure is performed.
+ *
+ * During IMSI detach processing: Request of IMSI detach is ignored.
+ *
+ * Any other state: The special 'delay_detach' flag is set only. If set, at any
+ * state transition we will clear the flag and restart the procedure again.
+ *
+ * The procedure is not spec conform, but always succeeds.
+ *
+ */
+
+/* Notes on Service states:
+ *
+ * There are two PLMN search states:
+ *
+ * - PLMN SEARCH NORMAL
+ * - PLMN SEARCH
+ *
+ * They are entered, if: (4.2.1.2)
+ * - ME is switched on
+ * - SIM is inserted
+ * - user has asked PLMN selection in certain Service states
+ * - coverage is lost in certain Service states
+ * - roaming is denied
+ * - (optionally see 4.2.1.2)
+ *
+ * PLMN SEARCH NORMAL state is then entered, if all these conditions are met:
+ * - SIM is valid
+ * - SIM state is U1
+ * - SIM LAI valid
+ * - cell selected
+ * - cell == SIM LAI
+ *
+ * Otherwhise PLMN SEARCH is entered.
+ *
+ * During PLMN SEARCH NORMAL state: (4.2.2.5)
+ * - on expirery of T3211 or T3213: Perform location update, when back
+ * to NORMAL SERVICE state.
+ * - on expirery of T3212: Perform periodic location update, when back
+ * to NORMAL SERVICE state.
+ * - perform IMSI detach
+ * - perform MM connections
+ * - respond to paging (if possible)
+ *
+ * During PLMN SEARCH state: (4.2.2.6)
+ * - reject MM connection except for emergency calls
+ *
+ *
+ * The NO CELL AVAILABLE state is entered, if:
+ * - no cell found during PLMN search
+ *
+ * During NO CELL AVAILABLE state:
+ * - reject any MM connection
+ *
+ *
+ * The NO IMSI state is entered if:
+ * - SIM is invalid
+ * - and cell is selected during PLMN SEARCH states
+ *
+ * During NO IMSO state: (4.2.2.4)
+ * - reject MM connection except for emergency calls
+ *
+ *
+ * The LIMITED SERVICE state is entered if:
+ * - SIM is valid
+ * - and SIM state is U3
+ * - and cell is selected
+ *
+ * During LIMITED SERVICE state: (4.2.2.3)
+ * - reject MM connection except for emergency calls
+ * - perform location update, if new LAI is entered
+ *
+ *
+ * The LOCATION UPDATE NEEDED state is entered if:
+ * - SIM is valid
+ * - and location update must be performed for any reason
+ *
+ * During LOCATION UPDATE NEEDED state:
+ * - reject MM connection except for emergency calls
+ *
+ * This state is left if location update is possible and directly enter
+ * state ATTEMPTING TO UPDATE and trigger location update.
+ * The function gsm48_mm_loc_upd_possible() is used to check this on state
+ * change.
+ *
+ *
+ * The ATTEMPTING TO UPDATE state is entered if:
+ * - SIM is valid
+ * - and SIM state is U2
+ * - and cell is selected
+ *
+ * During ATTEMPTING TO UPDATE state: (4.2.2.2)
+ * - on expirery of T3211 or T3213: Perform location updated
+ * - on expirery of T3212: Perform location updated
+ * - on change of LAI: Perform location update
+ * - (abnormal cases unsupported)
+ * - accept MM connection for emergency calls
+ * - trigger location update on any other MM connection
+ * - respond to paging (with IMSI only, because in U2 TMSI is not valid)
+ *
+ *
+ * The NORMAL SERVICE state is entered if:
+ * - SIM is valid
+ * - and SIM state is U1
+ * - and cell is selected
+ * - and SIM LAI == cell
+ *
+ * During NORMAL SERVICE state: (4.2.2.1)
+ * - on expirery of T3211 or T3213: Perform location updated
+ * - on expirery of T3212: Perform location updated
+ * - on change of LAI: Perform location update
+ * - perform IMSI detach
+ * - perform MM connections
+ * - respond to paging
+ *
+ *
+ * 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.
+ *
+ *
+ * gsm48_mm_return_idle() is used to select the Service state when returning
+ * to MM IDLE state after cell reselection.
+ *
+ *
+ * If cell selection process indicates NO_CELL_FOUND:
+ *
+ * - NO CELL AVAILABLE state is entered, if not already.
+ *
+ * gsm48_mm_no_cell_found() is used to select the Service state.
+ *
+ *
+ * If cell selection process indicates CELL_SELECTED:
+ *
+ * - NO IMSI state is entered, if no SIM valid.
+ * - Otherwise NORMAL SERVICES state is entered, if
+ * SIM state is U1, SIM LAI == cell, IMSI is attached, T3212 not expired.
+ * - Otherwise NORMAL SERVICES state is entered, if
+ * SIM state is U1, SIM LAI == cell, attach not required, T3212 not expired.
+ * - Otherwise LIMITED SERVICE state is entered, if
+ * CS mode is automatic, cell is forbidden PLMN or forbidden LA.
+ * - Otherwise LIMITED SERVICE state is entered, if
+ * CS mode is manual, cell is not the selected one.
+ * - Otherwise LOCATION UPDATE NEEDED state is entered.
+ *
+ * gsm48_mm_cell_selected() is used to select the Service state.
+ *
+ */
+
+/*
+ * support functions
+ */
+
+/* decode network name */
+static int decode_network_name(char *name, int name_len,
+ const uint8_t *lv)
+{
+ uint8_t in_len = lv[0];
+ int length, padding;
+
+ name[0] = '\0';
+ if (in_len < 1)
+ return -EINVAL;
+
+ /* must be CB encoded */
+ if ((lv[1] & 0x70) != 0x00)
+ return -ENOTSUP;
+
+ padding = lv[1] & 0x03;
+ length = ((in_len - 1) * 8 - padding) / 7;
+ if (length <= 0)
+ return 0;
+ if (length >= name_len)
+ length = name_len - 1;
+ gsm_7bit_decode(name, lv + 2, length);
+ name[length] = '\0';
+
+ return length;
+}
+
+/* encode 'mobile identity' */
+int gsm48_encode_mi(uint8_t *buf, struct msgb *msg, struct osmocom_ms *ms,
+ uint8_t mi_type)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm_settings *set = &ms->settings;
+ uint8_t *ie;
+
+ switch(mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ gsm48_generate_mid_from_tmsi(buf, subscr->tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_generate_mid_from_imsi(buf, subscr->imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ gsm48_generate_mid_from_imsi(buf, set->imei);
+ break;
+ case GSM_MI_TYPE_IMEISV:
+ gsm48_generate_mid_from_imsi(buf, set->imeisv);
+ break;
+ case GSM_MI_TYPE_NONE:
+ default:
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = 1;
+ buf[2] = 0xf0;
+ break;
+ }
+ /* alter MI type */
+ buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | mi_type;
+
+ if (msg) {
+ /* MI as LV */
+ ie = msgb_put(msg, 1 + buf[1]);
+ memcpy(ie, buf + 1, 1 + buf[1]);
+ }
+
+ return 0;
+}
+
+/* encode 'classmark 1' */
+int gsm48_encode_classmark1(struct gsm48_classmark1 *cm, uint8_t rev_lev,
+ uint8_t es_ind, uint8_t a5_1, uint8_t pwr_lev)
+{
+ memset(cm, 0, sizeof(*cm));
+ cm->rev_lev = rev_lev;
+ cm->es_ind = es_ind;
+ cm->a5_1 = a5_1;
+ cm->pwr_lev = pwr_lev;
+
+ return 0;
+}
+
+/*
+ * timers
+ */
+
+static void timeout_mm_t3210(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DMM, LOGL_INFO, "timer T3210 (loc. upd. timeout) has fired\n");
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3210, NULL);
+}
+
+static void timeout_mm_t3211(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DSUM, LOGL_INFO, "Location update retry\n");
+ LOGP(DMM, LOGL_INFO, "timer T3211 (loc. upd. retry delay) has fired\n");
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3211, NULL);
+}
+
+static void timeout_mm_t3212(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DSUM, LOGL_INFO, "Periodic location update\n");
+ LOGP(DMM, LOGL_INFO, "timer T3212 (periodic loc. upd. delay) has "
+ "fired\n");
+
+ /* reset attempt counter when attempting to update (4.4.4.5) */
+ if (mm->state == GSM48_MM_ST_MM_IDLE
+ && mm->substate == GSM48_MM_SST_ATTEMPT_UPDATE)
+ mm->lupd_attempt = 0;
+
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3212, NULL);
+}
+
+static void timeout_mm_t3213(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DSUM, LOGL_INFO, "Location update retry\n");
+ LOGP(DMM, LOGL_INFO, "timer T3213 (delay after RA failure) has "
+ "fired\n");
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3213, NULL);
+}
+
+static void timeout_mm_t3230(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DMM, LOGL_INFO, "timer T3230 (MM connection timeout) has "
+ "fired\n");
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3230, NULL);
+}
+
+static void timeout_mm_t3220(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DMM, LOGL_INFO, "timer T3220 (IMSI detach keepalive) has "
+ "fired\n");
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3220, NULL);
+}
+
+static void timeout_mm_t3240(void *arg)
+{
+ struct gsm48_mmlayer *mm = arg;
+
+ LOGP(DMM, LOGL_INFO, "timer T3240 (RR release timeout) has fired\n");
+ gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3240, NULL);
+}
+
+static void start_mm_t3210(struct gsm48_mmlayer *mm)
+{
+ LOGP(DMM, LOGL_INFO, "starting T3210 (loc. upd. timeout) with %d.%d "
+ "seconds\n", GSM_T3210_MS);
+ mm->t3210.cb = timeout_mm_t3210;
+ mm->t3210.data = mm;
+ bsc_schedule_timer(&mm->t3210, GSM_T3210_MS);
+}
+
+static void start_mm_t3211(struct gsm48_mmlayer *mm)
+{
+ LOGP(DMM, LOGL_INFO, "starting T3211 (loc. upd. retry delay) with "
+ "%d.%d seconds\n", GSM_T3211_MS);
+ mm->t3211.cb = timeout_mm_t3211;
+ mm->t3211.data = mm;
+ bsc_schedule_timer(&mm->t3211, GSM_T3211_MS);
+}
+
+static void start_mm_t3212(struct gsm48_mmlayer *mm, int sec)
+{
+ /* don't start, if is not available */
+ if (!sec)
+ return;
+
+ LOGP(DMM, LOGL_INFO, "starting T3212 (periodic loc. upd. delay) with "
+ "%d seconds\n", sec);
+ mm->t3212.cb = timeout_mm_t3212;
+ mm->t3212.data = mm;
+ bsc_schedule_timer(&mm->t3212, sec, 0);
+}
+
+static void start_mm_t3213(struct gsm48_mmlayer *mm)
+{
+ LOGP(DMM, LOGL_INFO, "starting T3213 (delay after RA failure) with "
+ "%d.%d seconds\n", GSM_T3213_MS);
+ mm->t3213.cb = timeout_mm_t3213;
+ mm->t3213.data = mm;
+ bsc_schedule_timer(&mm->t3213, GSM_T3213_MS);
+}
+
+static void start_mm_t3220(struct gsm48_mmlayer *mm)
+{
+ LOGP(DMM, LOGL_INFO, "starting T3220 (IMSI detach keepalive) with "
+ "%d.%d seconds\n", GSM_T3220_MS);
+ mm->t3220.cb = timeout_mm_t3220;
+ mm->t3220.data = mm;
+ bsc_schedule_timer(&mm->t3220, GSM_T3220_MS);
+}
+
+static void start_mm_t3230(struct gsm48_mmlayer *mm)
+{
+ LOGP(DMM, LOGL_INFO, "starting T3230 (MM connection timeout) with "
+ "%d.%d seconds\n", GSM_T3230_MS);
+ mm->t3230.cb = timeout_mm_t3230;
+ mm->t3230.data = mm;
+ bsc_schedule_timer(&mm->t3230, GSM_T3230_MS);
+}
+
+static void start_mm_t3240(struct gsm48_mmlayer *mm)
+{
+ LOGP(DMM, LOGL_INFO, "starting T3240 (RR release timeout) with %d.%d "
+ "seconds\n", GSM_T3240_MS);
+ mm->t3240.cb = timeout_mm_t3240;
+ mm->t3240.data = mm;
+ bsc_schedule_timer(&mm->t3240, GSM_T3240_MS);
+}
+
+static void stop_mm_t3210(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3210)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (loc. upd. timeout) "
+ "timer T3210\n");
+ bsc_del_timer(&mm->t3210);
+ }
+}
+
+static void stop_mm_t3211(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3211)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (loc. upd. retry "
+ "delay) timer T3211\n");
+ bsc_del_timer(&mm->t3211);
+ }
+}
+
+static void stop_mm_t3212(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3212)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (periodic loc. upd. "
+ "delay) timer T3212\n");
+ bsc_del_timer(&mm->t3212);
+ }
+}
+
+static void stop_mm_t3213(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3213)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (delay after RA "
+ "failure) timer T3213\n");
+ bsc_del_timer(&mm->t3213);
+ }
+}
+
+static void stop_mm_t3220(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3220)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (IMSI detach keepalive) "
+ "timer T3220\n");
+ bsc_del_timer(&mm->t3220);
+ }
+}
+
+static void stop_mm_t3230(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3230)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (MM connection timeout) "
+ "timer T3230\n");
+ bsc_del_timer(&mm->t3230);
+ }
+}
+
+static void stop_mm_t3240(struct gsm48_mmlayer *mm)
+{
+ if (bsc_timer_pending(&mm->t3240)) {
+ LOGP(DMM, LOGL_INFO, "stopping pending (RR release timeout) "
+ "timer T3240\n");
+ bsc_del_timer(&mm->t3240);
+ }
+}
+
+static void stop_mm_t3241(struct gsm48_mmlayer *mm)
+{
+ /* not implemented, not required */
+}
+
+/*
+ * messages
+ */
+
+/* names of MM events */
+static const struct value_string gsm48_mmevent_names[] = {
+ { GSM48_MM_EVENT_CELL_SELECTED, "MM_EVENT_CELL_SELECTED" },
+ { GSM48_MM_EVENT_NO_CELL_FOUND, "MM_EVENT_NO_CELL_FOUND" },
+ { GSM48_MM_EVENT_TIMEOUT_T3210, "MM_EVENT_TIMEOUT_T3210" },
+ { GSM48_MM_EVENT_TIMEOUT_T3211, "MM_EVENT_TIMEOUT_T3211" },
+ { GSM48_MM_EVENT_TIMEOUT_T3212, "MM_EVENT_TIMEOUT_T3212" },
+ { GSM48_MM_EVENT_TIMEOUT_T3213, "MM_EVENT_TIMEOUT_T3213" },
+ { GSM48_MM_EVENT_TIMEOUT_T3220, "MM_EVENT_TIMEOUT_T3220" },
+ { GSM48_MM_EVENT_TIMEOUT_T3230, "MM_EVENT_TIMEOUT_T3230" },
+ { GSM48_MM_EVENT_TIMEOUT_T3240, "MM_EVENT_TIMEOUT_T3240" },
+ { GSM48_MM_EVENT_IMSI_DETACH, "MM_EVENT_IMSI_DETACH" },
+ { GSM48_MM_EVENT_PAGING, "MM_EVENT_PAGING" },
+ { GSM48_MM_EVENT_AUTH_RESPONSE, "MM_EVENT_AUTH_RESPONSE" },
+ { GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" },
+ { GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" },
+ { 0, NULL }
+};
+
+const char *get_mmevent_name(int value)
+{
+ return get_value_string(gsm48_mmevent_names, value);
+}
+
+/* names of MM-SAP */
+static const struct value_string gsm48_mm_msg_names[] = {
+ { GSM48_MT_MM_IMSI_DETACH_IND, "MT_MM_IMSI_DETACH_IND" },
+ { GSM48_MT_MM_LOC_UPD_ACCEPT, "MT_MM_LOC_UPD_ACCEPT" },
+ { GSM48_MT_MM_LOC_UPD_REJECT, "MT_MM_LOC_UPD_REJECT" },
+ { GSM48_MT_MM_LOC_UPD_REQUEST, "MT_MM_LOC_UPD_REQUEST" },
+ { GSM48_MT_MM_AUTH_REJ, "MT_MM_AUTH_REJ" },
+ { GSM48_MT_MM_AUTH_REQ, "MT_MM_AUTH_REQ" },
+ { GSM48_MT_MM_AUTH_RESP, "MT_MM_AUTH_RESP" },
+ { GSM48_MT_MM_ID_REQ, "MT_MM_ID_REQ" },
+ { GSM48_MT_MM_ID_RESP, "MT_MM_ID_RESP" },
+ { GSM48_MT_MM_TMSI_REALL_CMD, "MT_MM_TMSI_REALL_CMD" },
+ { GSM48_MT_MM_TMSI_REALL_COMPL, "MT_MM_TMSI_REALL_COMPL" },
+ { GSM48_MT_MM_CM_SERV_ACC, "MT_MM_CM_SERV_ACC" },
+ { GSM48_MT_MM_CM_SERV_REJ, "MT_MM_CM_SERV_REJ" },
+ { GSM48_MT_MM_CM_SERV_ABORT, "MT_MM_CM_SERV_ABORT" },
+ { GSM48_MT_MM_CM_SERV_REQ, "MT_MM_CM_SERV_REQ" },
+ { GSM48_MT_MM_CM_SERV_PROMPT, "MT_MM_CM_SERV_PROMPT" },
+ { GSM48_MT_MM_CM_REEST_REQ, "MT_MM_CM_REEST_REQ" },
+ { GSM48_MT_MM_ABORT, "MT_MM_ABORT" },
+ { GSM48_MT_MM_NULL, "MT_MM_NULL" },
+ { GSM48_MT_MM_STATUS, "MT_MM_STATUS" },
+ { GSM48_MT_MM_INFO, "MT_MM_INFO" },
+ { 0, NULL }
+};
+
+const char *get_mm_name(int value)
+{
+ return get_value_string(gsm48_mm_msg_names, value);
+}
+
+/* names of MMxx-SAP */
+static const struct value_string gsm48_mmxx_msg_names[] = {
+ { GSM48_MMCC_EST_REQ, "MMCC_EST_REQ" },
+ { GSM48_MMCC_EST_IND, "MMCC_EST_IND" },
+ { GSM48_MMCC_EST_CNF, "MMCC_EST_CNF" },
+ { GSM48_MMCC_REL_REQ, "MMCC_REL_REQ" },
+ { GSM48_MMCC_REL_IND, "MMCC_REL_IND" },
+ { GSM48_MMCC_DATA_REQ, "MMCC_DATA_REQ" },
+ { GSM48_MMCC_DATA_IND, "MMCC_DATA_IND" },
+ { GSM48_MMCC_UNIT_DATA_REQ, "MMCC_UNIT_DATA_REQ" },
+ { GSM48_MMCC_UNIT_DATA_IND, "MMCC_UNIT_DATA_IND" },
+ { GSM48_MMCC_SYNC_IND, "MMCC_SYNC_IND" },
+ { GSM48_MMCC_REEST_REQ, "MMCC_REEST_REQ" },
+ { GSM48_MMCC_REEST_CNF, "MMCC_REEST_CNF" },
+ { GSM48_MMCC_ERR_IND, "MMCC_ERR_IND" },
+ { GSM48_MMCC_PROMPT_IND, "MMCC_PROMPT_IND" },
+ { GSM48_MMCC_PROMPT_REJ, "MMCC_PROMPT_REJ" },
+ { GSM48_MMSS_EST_REQ, "MMSS_EST_REQ" },
+ { GSM48_MMSS_EST_IND, "MMSS_EST_IND" },
+ { GSM48_MMSS_EST_CNF, "MMSS_EST_CNF" },
+ { GSM48_MMSS_REL_REQ, "MMSS_REL_REQ" },
+ { GSM48_MMSS_REL_IND, "MMSS_REL_IND" },
+ { GSM48_MMSS_DATA_REQ, "MMSS_DATA_REQ" },
+ { GSM48_MMSS_DATA_IND, "MMSS_DATA_IND" },
+ { GSM48_MMSS_UNIT_DATA_REQ, "MMSS_UNIT_DATA_REQ" },
+ { GSM48_MMSS_UNIT_DATA_IND, "MMSS_UNIT_DATA_IND" },
+ { GSM48_MMSS_REEST_REQ, "MMSS_REEST_REQ" },
+ { GSM48_MMSS_REEST_CNF, "MMSS_REEST_CNF" },
+ { GSM48_MMSS_ERR_IND, "MMSS_ERR_IND" },
+ { GSM48_MMSS_PROMPT_IND, "MMSS_PROMPT_IND" },
+ { GSM48_MMSS_PROMPT_REJ, "MMSS_PROMPT_REJ" },
+ { GSM48_MMSMS_EST_REQ, "MMSMS_EST_REQ" },
+ { GSM48_MMSMS_EST_IND, "MMSMS_EST_IND" },
+ { GSM48_MMSMS_EST_CNF, "MMSMS_EST_CNF" },
+ { GSM48_MMSMS_REL_REQ, "MMSMS_REL_REQ" },
+ { GSM48_MMSMS_REL_IND, "MMSMS_REL_IND" },
+ { GSM48_MMSMS_DATA_REQ, "MMSMS_DATA_REQ" },
+ { GSM48_MMSMS_DATA_IND, "MMSMS_DATA_IND" },
+ { GSM48_MMSMS_UNIT_DATA_REQ, "MMSMS_UNIT_DATA_REQ" },
+ { GSM48_MMSMS_UNIT_DATA_IND, "MMSMS_UNIT_DATA_IND" },
+ { GSM48_MMSMS_REEST_REQ, "MMSMS_REEST_REQ" },
+ { GSM48_MMSMS_REEST_CNF, "MMSMS_REEST_CNF" },
+ { GSM48_MMSMS_ERR_IND, "MMSMS_ERR_IND" },
+ { GSM48_MMSMS_PROMPT_IND, "MMSMS_PROMPT_IND" },
+ { GSM48_MMSMS_PROMPT_REJ, "MMSMS_PROMPT_REJ" },
+ { 0, NULL }
+};
+
+const char *get_mmxx_name(int value)
+{
+ return get_value_string(gsm48_mmxx_msg_names, value);
+}
+
+/* names of MMR-SAP */
+static const struct value_string gsm48_mmr_msg_names[] = {
+ { GSM48_MMR_REG_REQ, "MMR_REG_REQ" },
+ { GSM48_MMR_REG_CNF, "MMR_REG_CNF" },
+ { GSM48_MMR_NREG_REQ, "MMR_NREG_REQ" },
+ { GSM48_MMR_NREG_IND, "MMR_NREG_IND" },
+ { 0, NULL }
+};
+
+const char *get_mmr_name(int value)
+{
+ return get_value_string(gsm48_mmr_msg_names, value);
+}
+
+/* allocate GSM 04.08 message (MMxx-SAP) */
+struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref,
+ uint8_t transaction_id)
+{
+ struct msgb *msg;
+ struct gsm48_mmxx_hdr *mmh;
+
+ msg = msgb_alloc_headroom(MMXX_ALLOC_SIZE+MMXX_ALLOC_HEADROOM,
+ MMXX_ALLOC_HEADROOM, "GSM 04.08 MMxx");
+ if (!msg)
+ return NULL;
+
+ mmh = (struct gsm48_mmxx_hdr *)msgb_put(msg, sizeof(*mmh));
+ mmh->msg_type = msg_type;
+ mmh->ref = ref;
+ mmh->transaction_id = transaction_id;
+
+ return msg;
+}
+
+/* allocate MM event message */
+struct msgb *gsm48_mmevent_msgb_alloc(int msg_type)
+{
+ struct msgb *msg;
+ struct gsm48_mm_event *mme;
+
+ msg = msgb_alloc_headroom(sizeof(*mme), 0, "GSM 04.08 MM event");
+ if (!msg)
+ return NULL;
+
+ mme = (struct gsm48_mm_event *)msgb_put(msg, sizeof(*mme));
+ mme->msg_type = msg_type;
+
+ return msg;
+}
+
+/* allocate MMR message */
+struct msgb *gsm48_mmr_msgb_alloc(int msg_type)
+{
+ struct msgb *msg;
+ struct gsm48_mmr *mmr;
+
+ msg = msgb_alloc_headroom(sizeof(*mmr), 0, "GSM 04.08 MMR");
+ if (!msg)
+ return NULL;
+
+ mmr = (struct gsm48_mmr *)msgb_put(msg, sizeof(*mmr));
+ mmr->msg_type = msg_type;
+
+ return msg;
+}
+
+/* queue message (MMxx-SAP) */
+int gsm48_mmxx_upmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ msgb_enqueue(&mm->mmxx_upqueue, msg);
+
+ return 0;
+}
+
+/* queue message (MMR-SAP) */
+int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ msgb_enqueue(&mm->mmr_downqueue, msg);
+
+ return 0;
+}
+
+/* queue MM event message */
+int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ msgb_enqueue(&mm->event_queue, msg);
+
+ return 0;
+}
+
+/* dequeue messages (MMxx-SAP) */
+int gsm48_mmxx_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct msgb *msg;
+ struct gsm48_mmxx_hdr *mmh;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&mm->mmxx_upqueue))) {
+ mmh = (struct gsm48_mmxx_hdr *) msg->data;
+ switch (mmh->msg_type & GSM48_MMXX_MASK) {
+ case GSM48_MMCC_CLASS:
+ gsm48_rcv_cc(ms, msg);
+ break;
+#if 0
+ case GSM48_MMSS_CLASS:
+ gsm48_rcv_ss(ms, msg);
+ break;
+ case GSM48_MMSMS_CLASS:
+ gsm48_rcv_sms(ms, msg);
+ break;
+#endif
+ }
+ msgb_free(msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/* dequeue messages (MMR-SAP) */
+int gsm48_mmr_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct msgb *msg;
+ struct gsm48_mmr *mmr;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&mm->mmr_downqueue))) {
+ mmr = (struct gsm48_mmr *) msg->data;
+ gsm48_rcv_mmr(ms, msg);
+ msgb_free(msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/* dequeue messages (RR-SAP) */
+int gsm48_rr_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&mm->rr_upqueue))) {
+ /* msg is freed there */
+ gsm48_rcv_rr(ms, msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/* dequeue MM event messages */
+int gsm48_mmevent_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mm_event *mme;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&mm->event_queue))) {
+ mme = (struct gsm48_mm_event *) msg->data;
+ gsm48_mm_ev(ms, mme->msg_type, msg);
+ msgb_free(msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/* push RR header and send to RR */
+static int gsm48_mm_to_rr(struct osmocom_ms *ms, struct msgb *msg,
+ int msg_type, uint8_t cause)
+{
+ struct gsm48_rr_hdr *rrh;
+
+ /* push RR header */
+ msgb_push(msg, sizeof(struct gsm48_rr_hdr));
+ rrh = (struct gsm48_rr_hdr *) msg->data;
+ rrh->msg_type = msg_type;
+ rrh->cause = cause;
+
+ /* send message to RR */
+ return gsm48_rr_downmsg(ms, msg);
+}
+
+/*
+ * state transition
+ */
+
+const char *gsm48_mm_state_names[] = {
+ "NULL",
+ "undefined 1",
+ "undefined 2",
+ "location updating initiated",
+ "undefined 4",
+ "wait for outgoing MM connection",
+ "MM connection active",
+ "IMSI detach initiated",
+ "process CM service prompt",
+ "wait for network command",
+ "location updating reject",
+ "undefined 11",
+ "undefined 12",
+ "wait for RR connection (location updating)",
+ "wait for RR connection (MM connection)",
+ "wait for RR connection (IMSI detach)",
+ "undefined 16",
+ "wait for re-establishment",
+ "wait for RR connection active",
+ "MM idle",
+ "wait for additional outgoing MM connection",
+ "MM_CONN_ACTIVE_VGCS",
+ "WAIT_RR_CONN_VGCS",
+ "location updating pending",
+ "IMSI detach pending",
+ "RR connection release not allowed"
+};
+
+const char *gsm48_mm_substate_names[] = {
+ "NULL",
+ "normal service",
+ "attempting to update",
+ "limited service",
+ "no IMSI",
+ "no cell available",
+ "location updating needed",
+ "PLMN search",
+ "PLMN search (normal)",
+ "RX_VGCS_NORMAL",
+ "RX_VGCS_LIMITED"
+};
+
+/* change state from LOCATION UPDATE NEEDED to ATTEMPTING TO UPDATE */
+static int gsm48_mm_loc_upd_possible(struct gsm48_mmlayer *mm)
+{
+ // TODO: check if really possible
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_ATTEMPT_UPDATE);
+ return gsm48_mm_loc_upd_normal(mm->ms, NULL);
+}
+
+/* Set new MM state, also new substate in case of MM IDLE state. */
+static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate)
+{
+ /* IDLE -> IDLE */
+ if (mm->state == GSM48_MM_ST_MM_IDLE && state == mm->state)
+ LOGP(DMM, LOGL_INFO, "new MM IDLE state %s -> %s\n",
+ gsm48_mm_substate_names[mm->substate],
+ gsm48_mm_substate_names[substate]);
+ /* IDLE -> non-IDLE */
+ else if (mm->state == GSM48_MM_ST_MM_IDLE)
+ LOGP(DMM, LOGL_INFO, "new state MM IDLE, %s -> %s\n",
+ gsm48_mm_substate_names[mm->substate],
+ gsm48_mm_state_names[state]);
+ /* non-IDLE -> IDLE */
+ else if (state == GSM48_MM_ST_MM_IDLE)
+ LOGP(DMM, LOGL_INFO, "new state %s -> MM IDLE, %s\n",
+ gsm48_mm_state_names[mm->state],
+ gsm48_mm_substate_names[substate]);
+ /* non-IDLE -> non-IDLE */
+ else
+ LOGP(DMM, LOGL_INFO, "new state %s -> %s\n",
+ gsm48_mm_state_names[mm->state],
+ gsm48_mm_state_names[state]);
+
+ /* remember most recent substate */
+ if (mm->state == GSM48_MM_ST_MM_IDLE)
+ mm->mr_substate = mm->substate;
+
+ mm->state = state;
+ mm->substate = substate;
+
+ /* resend detach event, if flag is set */
+ if (state == GSM48_MM_ST_MM_IDLE && mm->delay_detach) {
+ struct msgb *nmsg;
+
+ mm->delay_detach = 0;
+
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH);
+ if (!nmsg)
+ return;
+ gsm48_mmevent_msg(mm->ms, nmsg);
+ }
+
+ /* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */
+ if (state == GSM48_MM_ST_MM_IDLE
+ && (substate == GSM48_MM_SST_NORMAL_SERVICE
+ || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) {
+ /* start periodic location update timer */
+ if (!bsc_timer_pending(&mm->t3212))
+ start_mm_t3212(mm, mm->t3212_value);
+ /* perform pending location update */
+ if (mm->lupd_retry) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. pending (type %d)\n",
+ mm->lupd_type);
+ mm->lupd_retry = 0;
+ gsm48_mm_loc_upd(mm->ms, NULL);
+ /* must exit, because this function can be called
+ * recursively
+ */
+ return;
+ }
+ if (mm->lupd_periodic) {
+ struct gsm48_sysinfo *s = &mm->ms->cellsel.sel_si;
+
+ LOGP(DMM, LOGL_INFO, "Periodic loc. upd. pending "
+ "(type %d)\n", mm->lupd_type);
+ mm->lupd_periodic = 0;
+ if (s->t3212)
+ gsm48_mm_loc_upd_periodic(mm->ms, NULL);
+ else
+ LOGP(DMM, LOGL_INFO, "but not requred\n");
+ /* must exit, because this function can be called
+ * recursively
+ */
+ return;
+ }
+ }
+
+ /* check if location update is possible */
+ if (state == GSM48_MM_ST_MM_IDLE
+ && substate == GSM48_MM_SST_LOC_UPD_NEEDED) {
+ gsm48_mm_loc_upd_possible(mm);
+ /* must exit, because this function can be called recursively */
+ return;
+ }
+}
+
+/* return PLMN SEARCH or PLMN SEARCH NORMAL state */
+static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ /* SIM not inserted */
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
+ "no SIM.\n");
+ return GSM48_MM_SST_PLMN_SEARCH;
+ }
+
+ /* SIM not updated */
+ if (subscr->ustate != GSM_SIM_U1_UPDATED) {
+ LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
+ "SIM not updated.\n");
+ return GSM48_MM_SST_PLMN_SEARCH;
+ }
+ if (!subscr->lai_valid) {
+ LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
+ "LAI in SIM not valid.\n");
+ return GSM48_MM_SST_PLMN_SEARCH;
+ }
+
+ /* no cell selected */
+ if (!cs->selected) {
+ LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
+ "no cell selected.\n");
+ return GSM48_MM_SST_PLMN_SEARCH;
+ }
+
+ /* selected cell's LAI not equal to LAI stored on the sim */
+ if (cs->sel_mcc != subscr->lai_mcc
+ || cs->sel_mnc != subscr->lai_mnc
+ || cs->sel_lac != subscr->lai_lac) {
+ LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because "
+ "LAI of selected cell (MCC %s MNC %s LAC 0x%04x) "
+ "!= LAI in SIM (MCC %s MNC %s LAC 0x%04x).\n",
+ gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc),
+ cs->sel_lac, gsm_print_mcc(subscr->lai_mcc),
+ gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac);
+ return GSM48_MM_SST_PLMN_SEARCH;
+ }
+
+ /* SIM is updated in this LA */
+ LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH NORMAL state.\n");
+ return GSM48_MM_SST_PLMN_SEARCH_NORMAL;
+}
+
+/* 4.2.3 when returning to MM IDLE state, this function is called */
+static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+
+ /* 4.4.4.9 start T3211 when RR is released */
+ if (mm->start_t3211) {
+ LOGP(DMM, LOGL_INFO, "Starting T3211 after RR release.\n");
+ mm->start_t3211 = 0;
+ start_mm_t3211(mm);
+ }
+
+ /* return from location update with "Roaming not allowed" */
+ if (mm->state == GSM48_MM_ST_LOC_UPD_REJ && mm->lupd_rej_cause == 13) {
+ LOGP(DMM, LOGL_INFO, "Roaming not allowed as returning to "
+ "MM IDLE\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ gsm48_mm_set_plmn_search(ms));
+
+ return 0;
+ }
+
+ /* no SIM present or invalid */
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_INFO, "SIM invalid as returning to MM IDLE\n");
+
+ /* stop periodic location updating */
+ mm->lupd_pending = 0;
+ stop_mm_t3212(mm); /* 4.4.2 */
+
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_IMSI);
+
+ return 0;
+ }
+
+ /* selected cell equals the registered LAI */
+ if (subscr->lai_valid
+ && cs->sel_mcc == subscr->lai_mcc
+ && cs->sel_mnc == subscr->lai_mnc
+ && cs->sel_lac == subscr->lai_lac) {
+ LOGP(DMM, LOGL_INFO, "We are in registered LAI as returning "
+ "to MM IDLE\n");
+ /* if SIM not updated (abnormal case as described in 4.4.4.9) */
+ if (subscr->ustate != GSM_SIM_U1_UPDATED)
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_ATTEMPT_UPDATE);
+ else
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_NORMAL_SERVICE);
+
+ return 0;
+ }
+
+ /* location update allowed */
+ if (cs->state == GSM322_C3_CAMPED_NORMALLY) {
+ LOGP(DMM, LOGL_INFO, "We are camping normally as returning to "
+ "MM IDLE\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_LOC_UPD_NEEDED);
+ } else { /* location update not allowed */
+ LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning "
+ "to MM IDLE\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_LIMITED_SERVICE);
+ }
+
+ return 0;
+}
+
+/* 4.2.1.1 Service state PLMN SEARCH (NORMAL) is left if no cell found */
+static int gsm48_mm_no_cell_found(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_CELL_AVAIL);
+
+ return 0;
+}
+
+/* 4.2.1.1 Service state PLMN SEARCH (NORMAL) / NO CELL AVAILABLE is left
+ * if cell selected
+ */
+static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm322_plmn *plmn = &ms->plmn;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = &cs->sel_si;
+ struct gsm_settings *set = &ms->settings;
+
+ /* no SIM is inserted */
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_INFO, "SIM invalid as cell is selected.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_IMSI);
+
+ return 0;
+ }
+
+ /* SIM not updated in this LA */
+ if (subscr->ustate == GSM_SIM_U1_UPDATED
+ && subscr->lai_valid
+ && cs->sel_mcc == subscr->lai_mcc
+ && cs->sel_mnc == subscr->lai_mnc
+ && cs->sel_lac == subscr->lai_lac
+ && !mm->lupd_periodic) {
+ if (subscr->imsi_attached) {
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "Valid in location area.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_NORMAL_SERVICE);
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+ if (!s->att_allowed) {
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "Attachment not required.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_NORMAL_SERVICE);
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+ /* else, continue */
+ }
+
+ /* PLMN mode auto and selected cell is forbidden */
+ if (set->plmn_mode == PLMN_MODE_AUTO
+ && (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)
+ || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc,
+ cs->sel_lac))) {
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_LIMITED_SERVICE);
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* PLMN mode manual and selected cell not selected PLMN */
+ if (set->plmn_mode == PLMN_MODE_MANUAL
+ && (plmn->mcc != cs->sel_mcc
+ || plmn->mnc != cs->sel_mnc)) {
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "Selected cell not found.\n");
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ GSM48_MM_SST_LIMITED_SERVICE);
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* other cases */
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LOC_UPD_NEEDED);
+
+ return 0;
+}
+
+/* 4.2.1.2 Service state PLMN SEARCH (NORMAL) is entered */
+static int gsm48_mm_plmn_search(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, gsm48_mm_set_plmn_search(ms));
+
+ return 0;
+}
+
+/*
+ * init and exit
+ */
+
+/* initialize Mobility Management process */
+int gsm48_mm_init(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ memset(mm, 0, sizeof(*mm));
+ mm->ms = ms;
+
+ LOGP(DMM, LOGL_INFO, "init Mobility Management process\n");
+
+ /* 4.2.1.1 */
+ mm->state = GSM48_MM_ST_MM_IDLE;
+ mm->substate = gsm48_mm_set_plmn_search(ms);
+
+ /* init lists */
+ INIT_LLIST_HEAD(&mm->mm_conn);
+ INIT_LLIST_HEAD(&mm->rr_upqueue);
+ INIT_LLIST_HEAD(&mm->mmxx_upqueue);
+ INIT_LLIST_HEAD(&mm->mmr_downqueue);
+ INIT_LLIST_HEAD(&mm->event_queue);
+
+ return 0;
+}
+
+/* exit MM process */
+int gsm48_mm_exit(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mm_conn *conn;
+ struct msgb *msg;
+
+ LOGP(DMM, LOGL_INFO, "exit Mobility Management process\n");
+
+ /* flush lists */
+ while (!llist_empty(&mm->mm_conn)) {
+ conn = llist_entry(mm->mm_conn.next,
+ struct gsm48_mm_conn, list);
+ llist_del(&conn->list);
+ mm_conn_free(conn);
+ }
+ while ((msg = msgb_dequeue(&mm->rr_upqueue)))
+ msgb_free(msg);
+ while ((msg = msgb_dequeue(&mm->mmxx_upqueue)))
+ msgb_free(msg);
+ while ((msg = msgb_dequeue(&mm->mmr_downqueue)))
+ msgb_free(msg);
+ while ((msg = msgb_dequeue(&mm->event_queue)))
+ msgb_free(msg);
+
+ /* stop timers */
+ stop_mm_t3210(mm);
+ stop_mm_t3211(mm);
+ stop_mm_t3212(mm);
+ stop_mm_t3213(mm);
+ stop_mm_t3220(mm);
+ stop_mm_t3230(mm);
+ stop_mm_t3240(mm);
+
+ return 0;
+}
+
+/*
+ * MM connection management
+ */
+
+static const char *gsm48_mmxx_state_names[] = {
+ "IDLE",
+ "CONN_PEND",
+ "DEDICATED",
+ "CONN_SUSP",
+ "REESTPEND"
+};
+
+uint32_t mm_conn_new_ref = 1;
+
+/* new MM connection state */
+static void new_conn_state(struct gsm48_mm_conn *conn, int state)
+{
+ LOGP(DMM, LOGL_INFO, "(ref %d) new state %s -> %s\n", conn->ref,
+ gsm48_mmxx_state_names[conn->state],
+ gsm48_mmxx_state_names[state]);
+ conn->state = state;
+}
+
+/* find MM connection by protocol+ID */
+struct gsm48_mm_conn *mm_conn_by_id(struct gsm48_mmlayer *mm,
+ uint8_t proto, uint8_t transaction_id)
+{
+ struct gsm48_mm_conn *conn;
+
+ llist_for_each_entry(conn, &mm->mm_conn, list) {
+ if (conn->protocol == proto &&
+ conn->transaction_id == transaction_id)
+ return conn;
+ }
+ return NULL;
+}
+
+/* find MM connection by reference */
+struct gsm48_mm_conn *mm_conn_by_ref(struct gsm48_mmlayer *mm,
+ uint32_t ref)
+{
+ struct gsm48_mm_conn *conn;
+
+ llist_for_each_entry(conn, &mm->mm_conn, list) {
+ if (conn->ref == ref)
+ return conn;
+ }
+ return NULL;
+}
+
+/* create MM connection instance */
+static struct gsm48_mm_conn* mm_conn_new(struct gsm48_mmlayer *mm,
+ int proto, uint8_t transaction_id, uint32_t ref)
+{
+ struct gsm48_mm_conn *conn = talloc_zero(l23_ctx, struct gsm48_mm_conn);
+
+ if (!conn)
+ return NULL;
+
+ LOGP(DMM, LOGL_INFO, "New MM Connection (proto 0x%02x trans_id %d "
+ "ref %d)\n", proto, transaction_id, ref);
+
+ conn->mm = mm;
+ conn->state = GSM48_MMXX_ST_IDLE;
+ conn->transaction_id = transaction_id;
+ conn->protocol = proto;
+ conn->ref = ref;
+
+ llist_add(&conn->list, &mm->mm_conn);
+
+ return conn;
+}
+
+/* destroy MM connection instance */
+void mm_conn_free(struct gsm48_mm_conn *conn)
+{
+ LOGP(DMM, LOGL_INFO, "Freeing MM Connection\n");
+
+ new_conn_state(conn, GSM48_MMXX_ST_IDLE);
+
+ llist_del(&conn->list);
+
+ talloc_free(conn);
+}
+
+/* support function to release pending/all ongoing MM connections */
+static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any,
+ uint8_t cause, int error)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mm_conn *conn, *conn2;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ if (abort_any)
+ LOGP(DMM, LOGL_INFO, "Release any MM Connection\n");
+ else
+ LOGP(DMM, LOGL_INFO, "Release pending MM Connections\n");
+
+ /* release MM connection(s) */
+ llist_for_each_entry_safe(conn, conn2, &mm->mm_conn, list) {
+ /* abort any OR the pending connection */
+ if (abort_any || conn->state == GSM48_MMXX_ST_CONN_PEND) {
+ /* send MMxx-REL-IND */
+ nmsg = NULL;
+ switch(conn->protocol) {
+ case GSM48_PDISC_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(
+ error ? GSM48_MMCC_ERR_IND
+ : GSM48_MMCC_REL_IND, conn->ref,
+ conn->transaction_id);
+ break;
+ case GSM48_PDISC_NC_SS:
+ nmsg = gsm48_mmxx_msgb_alloc(
+ error ? GSM48_MMSS_ERR_IND
+ : GSM48_MMSS_REL_IND, conn->ref,
+ conn->transaction_id);
+ break;
+ case GSM48_PDISC_SMS:
+ nmsg = gsm48_mmxx_msgb_alloc(
+ error ? GSM48_MMSMS_ERR_IND
+ : GSM48_MMSMS_REL_IND, conn->ref,
+ conn->transaction_id);
+ break;
+ }
+ if (!nmsg) {
+ /* this should not happen */
+ mm_conn_free(conn);
+ continue; /* skip if not of CC type */
+ }
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = cause;
+ gsm48_mmxx_upmsg(ms, nmsg);
+
+ mm_conn_free(conn);
+ }
+ }
+ return 0;
+}
+
+/*
+ * process handlers (Common procedures)
+ */
+
+/* sending MM STATUS message */
+static int gsm48_mm_tx_mm_status(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+ uint8_t *reject_cause;
+
+ LOGP(DMM, LOGL_INFO, "MM STATUS (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+ reject_cause = msgb_put(nmsg, 1);
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_STATUS;
+ *reject_cause = cause;
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0);
+}
+
+/* 4.3.1.2 sending TMSI REALLOCATION COMPLETE message */
+static int gsm48_mm_tx_tmsi_reall_cpl(struct osmocom_ms *ms)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+
+ LOGP(DMM, LOGL_INFO, "TMSI REALLOCATION COMPLETE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_TMSI_REALL_COMPL;
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0);
+}
+
+/* 4.3.1 TMSI REALLOCATION COMMAND is received */
+static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) gh->data;
+ uint8_t mi_type, *mi;
+ uint32_t tmsi;
+
+ if (payload_len < sizeof(struct gsm48_loc_area_id) + 2) {
+ short_read:
+ LOGP(DMM, LOGL_NOTICE, "Short read of TMSI REALLOCATION "
+ "COMMAND message error.\n");
+ return -EINVAL;
+ }
+ /* LAI */
+ gsm48_decode_lai(lai, &subscr->lai_mcc, &subscr->lai_mnc,
+ &subscr->lai_lac);
+ /* MI */
+ mi = gh->data + sizeof(struct gsm48_loc_area_id);
+ mi_type = mi[1] & GSM_MI_TYPE_MASK;
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ if (payload_len + sizeof(struct gsm48_loc_area_id) < 6
+ || mi[0] < 5)
+ goto short_read;
+ memcpy(&tmsi, mi+2, 4);
+ subscr->tmsi = ntohl(tmsi);
+ subscr->tmsi_valid = 1;
+ LOGP(DMM, LOGL_INFO, "TMSI 0x%08x (%u) assigned.\n",
+ subscr->tmsi, subscr->tmsi);
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ subscr->tmsi_valid = 0;
+ LOGP(DMM, LOGL_INFO, "TMSI removed.\n");
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ default:
+ LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown MI "
+ "type %d.\n", mi_type);
+ gsm48_mm_tx_mm_status(ms, GSM48_REJECT_INCORRECT_MESSAGE);
+
+ return 0; /* don't store in SIM */
+ }
+
+#ifdef TODO
+ store / remove from sim
+#endif
+
+ return 0;
+}
+
+#ifndef TODO
+static int gsm48_mm_tx_auth_rsp(struct osmocom_ms *ms, struct msgb *msg);
+#endif
+
+/* 4.3.2.2 AUTHENTICATION REQUEST is received */
+static int gsm48_mm_rx_auth_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm48_auth_req *ar = (struct gsm48_auth_req *) gh->data;
+
+ if (payload_len < sizeof(struct gsm48_auth_req)) {
+ LOGP(DMM, LOGL_NOTICE, "Short read of AUTHENTICATION REQUEST "
+ "message error.\n");
+ return -EINVAL;
+ }
+
+ /* SIM is not available */
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_INFO, "AUTHENTICATION REQUEST without SIM\n");
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE);
+ }
+
+ LOGP(DMM, LOGL_INFO, "AUTHENTICATION REQUEST (seq %d)\n", ar->key_seq);
+
+ /* key_seq and random */
+#ifdef TODO
+ new key to sim:
+ (..., ar->key_seq, ar->rand);
+#else
+ /* Fake response */
+ struct msgb *nmsg;
+ struct gsm48_mm_event *nmme;
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ nmme = (struct gsm48_mm_event *)msgb_put(nmsg, sizeof(*nmme));
+ *((uint32_t *)nmme->sres) = 0x12345678;
+ gsm48_mm_tx_auth_rsp(ms, nmsg);
+ msgb_free(nmsg);
+#endif
+
+ /* wait for auth response event from SIM */
+ return 0;
+}
+
+/* 4.3.2.2 sending AUTHENTICATION RESPONSE */
+static int gsm48_mm_tx_auth_rsp(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mm_event *mme = (struct gsm48_mm_event *) msg->data;
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+ uint8_t *sres;
+
+ LOGP(DMM, LOGL_INFO, "AUTHENTICATION RESPONSE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_AUTH_RESP;
+
+ /* SRES */
+ sres = msgb_put(nmsg, 4);
+ memcpy(sres, mme->sres, 4);
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0);
+}
+
+/* 4.3.2.5 AUTHENTICATION REJECT is received */
+static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ LOGP(DMM, LOGL_INFO, "AUTHENTICATION REJECT\n");
+
+ stop_mm_t3212(mm); /* 4.4.2 */
+
+ /* SIM invalid */
+ subscr->sim_valid = 0;
+
+ /* TMSI and LAI invalid */
+ subscr->lai_valid = 0;
+ subscr->tmsi_valid = 0;
+
+ /* key is invalid */
+ subscr->key_seq = 7;
+
+ /* update status */
+ new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA);
+
+#ifdef TODO
+ sim: delete tmsi, lai
+ sim: delete key seq number
+ sim: set update status
+#endif
+
+ /* abort IMSI detach procedure */
+ if (mm->state == GSM48_MM_ST_IMSI_DETACH_INIT) {
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ /* abort RR connection */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *) msgb_put(nmsg, sizeof(*nrrh));
+ nrrh->cause = GSM48_RR_CAUSE_NORMAL;
+ gsm48_rr_downmsg(ms, nmsg);
+
+ /* CS process will trigger: return to MM IDLE / No SIM */
+ return 0;
+ }
+
+ return 0;
+}
+
+/* 4.3.3.1 IDENTITY REQUEST is received */
+static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ uint8_t mi_type;
+
+ if (payload_len < 1) {
+ LOGP(DMM, LOGL_NOTICE, "Short read of IDENTITY REQUEST message "
+ "error.\n");
+ return -EINVAL;
+ }
+ /* id type */
+ mi_type = *gh->data;
+
+ /* check if request can be fulfilled */
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST without SIM\n");
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE);
+ }
+ if (mi_type == GSM_MI_TYPE_TMSI && !subscr->tmsi_valid) {
+ LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST of TMSI, but we have no "
+ "TMSI\n");
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE);
+ }
+ LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST (mi_type %d)\n", mi_type);
+
+ return gsm48_mm_tx_id_rsp(ms, mi_type);
+}
+
+/* send IDENTITY RESPONSE message */
+static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+ uint8_t buf[11];
+
+ LOGP(DMM, LOGL_INFO, "IDENTITY RESPONSE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_ID_RESP;
+
+ /* MI */
+ gsm48_encode_mi(buf, nmsg, ms, mi_type);
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0);
+}
+
+/* 4.3.4.1 sending IMSI DETACH INDICATION message */
+static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm_support *sup = &ms->support;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+ uint8_t pwr_lev;
+ uint8_t buf[11];
+ struct gsm48_classmark1 cm;
+
+
+ LOGP(DMM, LOGL_INFO, "IMSI DETACH INDICATION\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_IMSI_DETACH_IND;
+
+ /* classmark 1 */
+ if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885)
+ pwr_lev = sup->pwr_lev_1800;
+ else
+ pwr_lev = sup->pwr_lev_900;
+ gsm48_encode_classmark1(&cm, sup->rev_lev, sup->es_ind, sup->a5_1,
+ pwr_lev);
+ msgb_v_put(nmsg, *((uint8_t *)&cm));
+ /* MI */
+ if (subscr->tmsi_valid) /* have TMSI ? */
+ gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_TMSI);
+ else
+ gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_IMSI);
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, rr_prim, RR_EST_CAUSE_OTHER_SDCCH);
+}
+
+/* detach has ended */
+static int gsm48_mm_imsi_detach_end(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+
+ LOGP(DMM, LOGL_INFO, "IMSI has been detached.\n");
+
+ /* stop IMSI detach timer (if running) */
+ stop_mm_t3220(mm);
+
+ /* update SIM */
+#ifdef TODO
+ sim: store BA list
+ sim: what else?:
+#endif
+
+ /* SIM invalid */
+ subscr->sim_valid = 0;
+
+ /* power off when IMSI is detached */
+ if (mm->power_off) {
+ l23_app_exit(ms);
+ exit (0);
+ }
+
+ /* send SIM remove event to gsm322 */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ /* CS process will trigger return to MM IDLE / No SIM */
+ return 0;
+}
+
+/* start an IMSI detach in MM IDLE */
+static int gsm48_mm_imsi_detach_start(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
+
+ /* we may silently finish IMSI detach */
+ if (!s->att_allowed || !subscr->imsi_attached) {
+ LOGP(DMM, LOGL_INFO, "IMSI detach not required.\n");
+
+ return gsm48_mm_imsi_detach_end(ms, msg);
+ }
+ LOGP(DMM, LOGL_INFO, "IMSI detach started (MM IDLE)\n");
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_IMSI_D, 0);
+
+ /* establish RR and send IMSI detach */
+ return gsm48_mm_tx_imsi_detach(ms, GSM48_RR_EST_REQ);
+}
+
+/* IMSI detach has been sent, wait for RR release */
+static int gsm48_mm_imsi_detach_sent(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* start T3220 (4.3.4.1) */
+ start_mm_t3220(mm);
+
+ LOGP(DMM, LOGL_INFO, "IMSI detach started (Wait for RR release)\n");
+
+ new_mm_state(mm, GSM48_MM_ST_IMSI_DETACH_INIT, 0);
+
+ return 0;
+}
+
+/* release MM connection and proceed with IMSI detach */
+static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
+
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ /* release all connections */
+ gsm48_mm_release_mm_conn(ms, 1, 16, 0);
+
+ /* wait for release of RR */
+ if (!s->att_allowed || !subscr->imsi_attached) {
+ LOGP(DMM, LOGL_INFO, "IMSI detach not required.\n");
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+
+ /* power off when IMSI is detached */
+ if (mm->power_off) {
+ l23_app_exit(ms);
+ exit (0);
+ }
+
+ return 0;
+ }
+
+ /* send IMSI detach */
+ gsm48_mm_tx_imsi_detach(ms, GSM48_RR_DATA_REQ);
+
+ /* go to sent state */
+ return gsm48_mm_imsi_detach_sent(ms, msg);
+}
+
+/* ignore ongoing IMSI detach */
+static int gsm48_mm_imsi_detach_ignore(struct osmocom_ms *ms, struct msgb *msg)
+{
+ return 0;
+}
+
+/* delay until state change (and then retry) */
+static int gsm48_mm_imsi_detach_delay(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ LOGP(DMM, LOGL_INFO, "IMSI detach delayed.\n");
+
+ /* remember to detach later */
+ mm->delay_detach = 1;
+
+ return 0;
+}
+
+/* 4.3.5.2 ABORT is received */
+static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ uint8_t reject_cause;
+
+ if (payload_len < 1) {
+ LOGP(DMM, LOGL_NOTICE, "Short read of ABORT message error.\n");
+ return -EINVAL;
+ }
+
+ reject_cause = *gh->data;
+
+ if (llist_empty(&mm->mm_conn)) {
+ LOGP(DMM, LOGL_NOTICE, "ABORT (cause #%d) while no MM "
+ "connection is established.\n", reject_cause);
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE);
+ } else {
+ LOGP(DMM, LOGL_NOTICE, "ABORT (cause #%d) while MM connection "
+ "is established.\n", reject_cause);
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ gsm48_mm_release_mm_conn(ms, 1, 16, 0);
+ }
+
+ if (reject_cause == GSM48_REJECT_ILLEGAL_ME) {
+ /* SIM invalid */
+ subscr->sim_valid = 0;
+
+ /* TMSI and LAI invalid */
+ subscr->lai_valid = 0;
+ subscr->tmsi_valid = 0;
+
+ /* key is invalid */
+ subscr->key_seq = 7;
+
+ /* update status */
+ new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA);
+
+#ifdef TODO
+ sim: delete tmsi, lai
+ sim: delete key seq number
+ sim: apply update state
+#endif
+
+ /* CS process will trigger: return to MM IDLE / No SIM */
+ return 0;
+ }
+
+ return 0;
+}
+
+/* 4.3.6.2 MM INFORMATION is received */
+static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+
+ if (payload_len < 0) {
+ LOGP(DMM, LOGL_NOTICE, "Short read of MM INFORMATION message "
+ "error.\n");
+ return -EINVAL;
+ }
+ tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0);
+
+ /* long name */
+ if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) {
+ decode_network_name(mm->name_long, sizeof(mm->name_long),
+ TLVP_VAL(&tp, GSM48_IE_NAME_LONG)-1);
+ }
+ /* short name */
+ if (TLVP_PRESENT(&tp, GSM48_IE_NAME_SHORT)) {
+ decode_network_name(mm->name_short, sizeof(mm->name_short),
+ TLVP_VAL(&tp, GSM48_IE_NAME_SHORT)-1);
+ }
+
+ return 0;
+}
+
+/*
+ * process handlers for Location Update + IMSI attach (MM specific procedures)
+ */
+
+/* 4.4.2 received sysinfo change event */
+static int gsm48_mm_sysinfo(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
+
+ /* t3212 not changed in these states */
+ if (mm->state == GSM48_MM_ST_MM_IDLE
+ && (mm->substate == GSM48_MM_SST_NO_CELL_AVAIL
+ || mm->substate == GSM48_MM_SST_LIMITED_SERVICE
+ || mm->substate == GSM48_MM_SST_PLMN_SEARCH
+ || mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL))
+ return 0;
+
+ /* new periodic location update timer timeout */
+ if (s->t3212 && s->t3212 != mm->t3212_value) {
+ if (bsc_timer_pending(&mm->t3212)) {
+ int t;
+ struct timeval current_time;
+
+ /* get rest time */
+ gettimeofday(&current_time, NULL);
+ t = mm->t3212.timeout.tv_sec - current_time.tv_sec;
+ if (t < 0)
+ t = 0;
+ LOGP(DMM, LOGL_INFO, "New T3212 while timer is running "
+ "(value %d rest %d)\n", s->t3212, t);
+
+ /* rest time modulo given value */
+ mm->t3212.timeout.tv_sec = current_time.tv_sec
+ + (t % s->t3212);
+ } else {
+ uint32_t rand = random();
+
+ LOGP(DMM, LOGL_INFO, "New T3212 while timer is not "
+ "running (value %d)\n", s->t3212);
+
+ /* value between 0 and given value */
+ start_mm_t3212(mm, rand % (s->t3212 + 1));
+ }
+ mm->t3212_value = s->t3212;
+ }
+
+ return 0;
+}
+
+/* 4.4.4.1 (re)start location update
+ *
+ * this function is called by
+ * - normal location update
+ * - periodic location update
+ * - imsi attach (normal loc. upd. function)
+ * - retry timers (T3211 and T3213)
+ */
+static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = &cs->sel_si;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+ int msg_type;
+
+ /* (re)start only if we still require location update */
+ if (!mm->lupd_pending) {
+ LOGP(DMM, LOGL_INFO, "No loc. upd. pending.\n");
+ return 0;
+ }
+
+ /* must camp normally */
+ if (cs->state != GSM322_C3_CAMPED_NORMALLY) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. not camping normally.\n");
+ msg_type = GSM322_EVENT_REG_FAILED;
+ stop:
+ LOGP(DSUM, LOGL_INFO, "Location update not possible\n");
+ mm->lupd_pending = 0;
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(msg_type);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+ return 0;
+ }
+
+ /* if LAI is forbidden, don't start */
+ if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n");
+ msg_type = GSM322_EVENT_ROAMING_NA;
+ goto stop;
+ }
+ if (gsm322_is_forbidden_la(ms, cs->sel_mcc,
+ cs->sel_mnc, cs->sel_lac)) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n");
+ msg_type = GSM322_EVENT_ROAMING_NA;
+ goto stop;
+ }
+
+ /* 4.4.4.9 if cell is barred, don't start */
+ if ((!subscr->acc_barr && s->cell_barr)
+ || (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) &
+ (s->class_barr ^ 0xffff)))) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n");
+ msg_type = GSM322_EVENT_ROAMING_NA;
+ goto stop;
+ }
+
+ mm->lupd_mcc = cs->sel_mcc;
+ mm->lupd_mnc = cs->sel_mnc;
+ mm->lupd_lac = cs->sel_lac;
+
+ LOGP(DSUM, LOGL_INFO, "Perform location update (MCC %s, MNC %s "
+ "LAC 0x%04x)\n", gsm_print_mcc(mm->lupd_mcc),
+ gsm_print_mnc(mm->lupd_mnc), mm->lupd_lac);
+
+ return gsm48_mm_tx_loc_upd_req(ms);
+}
+
+/* initiate a normal location update / imsi attach */
+static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = &cs->sel_si;
+ struct msgb *nmsg;
+
+ /* in case we already have a location update going on */
+ if (mm->lupd_pending) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. already pending.\n");
+
+ return -EBUSY;
+ }
+
+ /* no location update, if limited service */
+ if (cs->state != GSM322_C3_CAMPED_NORMALLY) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed.\n");
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* if location update is not required */
+ if (subscr->ustate == GSM_SIM_U1_UPDATED
+ && cs->selected
+ && cs->sel_mcc == subscr->lai_mcc
+ && cs->sel_mnc == subscr->lai_mnc
+ && cs->sel_lac == subscr->lai_lac
+ && (subscr->imsi_attached
+ || !s->att_allowed)) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. not required.\n");
+ subscr->imsi_attached = 1;
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return 0;
+ }
+
+ /* 4.4.3 is attachment required? */
+ if (subscr->ustate == GSM_SIM_U1_UPDATED
+ && cs->selected
+ && cs->sel_mcc == subscr->lai_mcc
+ && cs->sel_mnc == subscr->lai_mnc
+ && cs->sel_lac == subscr->lai_lac
+ && !subscr->imsi_attached
+ && s->att_allowed) {
+ /* do location update for IMSI attach */
+ LOGP(DMM, LOGL_INFO, "Do Loc. upd. for IMSI attach.\n");
+ mm->lupd_type = 2;
+ } else {
+ /* do normal location update */
+ LOGP(DMM, LOGL_INFO, "Do normal Loc. upd.\n");
+ mm->lupd_type = 0;
+ }
+
+ /* start location update */
+ mm->lupd_attempt = 0;
+ mm->lupd_pending = 1;
+ mm->lupd_ra_failure = 0;
+
+ return gsm48_mm_loc_upd(ms, msg);
+}
+
+/* initiate a periodic location update */
+static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* in case we already have a location update going on */
+ if (mm->lupd_pending) {
+ LOGP(DMM, LOGL_INFO, "Loc. upd. already pending.\n");
+ return -EBUSY;
+ }
+
+ /* start periodic location update */
+ mm->lupd_type = 1;
+ mm->lupd_pending = 1;
+ mm->lupd_ra_failure = 0;
+
+ return gsm48_mm_loc_upd(ms, msg);
+}
+
+/* ignore location update */
+static int gsm48_mm_loc_upd_ignore(struct osmocom_ms *ms, struct msgb *msg)
+{
+ return 0;
+}
+
+/* 9.2.15 send LOCATION UPDATING REQUEST message */
+static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_support *sup = &ms->support;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+ struct gsm48_loc_upd_req *nlu; /* NOTE: mi_len is part of struct */
+ uint8_t pwr_lev;
+ uint8_t buf[11];
+
+ LOGP(DMM, LOGL_INFO, "LOCATION UPDATING REQUEST\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+ nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu));
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST;
+
+ /* location updating type */
+ nlu->type = mm->lupd_type;
+ /* cipering key */
+ nlu->key_seq = subscr->key_seq;
+ /* LAI (use last SIM stored LAI) */
+ gsm48_generate_lai(&nlu->lai,
+ subscr->lai_mcc, subscr->lai_mnc, subscr->lai_lac);
+ /* classmark 1 */
+ if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885)
+ pwr_lev = sup->pwr_lev_1800;
+ else
+ pwr_lev = sup->pwr_lev_900;
+ gsm48_encode_classmark1(&nlu->classmark1, sup->rev_lev, sup->es_ind,
+ sup->a5_1, pwr_lev);
+ /* MI */
+ if (subscr->tmsi_valid) /* have TMSI ? */
+ gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI);
+ else
+ gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI);
+ msgb_put(nmsg, buf[1]); /* length is part of nlu */
+ memcpy(&nlu->mi_len, buf + 1, 1 + buf[1]);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_LUPD, 0);
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_EST_REQ, RR_EST_CAUSE_LOC_UPD);
+}
+
+/* 4.4.4.1 RR is esablised during location update */
+static int gsm48_mm_est_loc_upd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* start location update timer */
+ start_mm_t3210(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_LOC_UPD_INIT, 0);
+
+ return 0;
+}
+
+/* 4.4.4.6 LOCATION UPDATING ACCEPT is received */
+static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) gh->data;
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct tlv_parsed tp;
+ struct msgb *nmsg;
+
+ if (payload_len < sizeof(struct gsm48_loc_area_id)) {
+ short_read:
+ LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING ACCEPT "
+ "message error.\n");
+ return -EINVAL;
+ }
+ tlv_parse(&tp, &gsm48_mm_att_tlvdef,
+ gh->data + sizeof(struct gsm48_loc_area_id),
+ payload_len - sizeof(struct gsm48_loc_area_id), 0, 0);
+
+ /* update has finished */
+ mm->lupd_pending = 0;
+
+ /* RA was successfull */
+ mm->lupd_ra_failure = 0;
+
+ /* stop periodic location updating timer */
+ stop_mm_t3212(mm); /* 4.4.2 */
+
+ /* LAI */
+ subscr->lai_valid = 1;
+ gsm48_decode_lai(lai, &subscr->lai_mcc, &subscr->lai_mnc,
+ &subscr->lai_lac);
+
+ /* stop location update timer */
+ stop_mm_t3210(mm);
+
+ /* reset attempt counter */
+ mm->lupd_attempt = 0;
+
+ /* mark SIM as attached */
+ subscr->imsi_attached = 1;
+
+ /* set the status in the sim to updated */
+ new_sim_ustate(subscr, GSM_SIM_U1_UPDATED);
+#ifdef TODO
+ sim: apply update state
+#endif
+
+ /* set last registered PLMN */
+ subscr->plmn_valid = 1;
+ subscr->plmn_mcc = subscr->lai_mcc;
+ subscr->plmn_mnc = subscr->lai_mnc;
+#ifdef TODO
+ sim: store plmn
+#endif
+
+ LOGP(DSUM, LOGL_INFO, "Location update accepted\n");
+ LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (mcc %s mnc %s "
+ "lac 0x%04x)\n", gsm_print_mcc(subscr->lai_mcc),
+ gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac);
+
+ /* remove LA from forbidden list */
+ gsm322_del_forbidden_la(ms, subscr->lai_mcc, subscr->lai_mnc,
+ subscr->lai_lac);
+
+ /* MI */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
+ const uint8_t *mi;
+ uint8_t mi_type;
+ uint32_t tmsi;
+
+ mi = TLVP_VAL(&tp, GSM48_IE_MOBILE_ID)-1;
+ if (mi[0] < 1)
+ goto short_read;
+ mi_type = mi[1] & GSM_MI_TYPE_MASK;
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ if (payload_len + sizeof(struct gsm48_loc_area_id) < 6
+ || mi[0] < 5)
+ goto short_read;
+ memcpy(&tmsi, mi+2, 4);
+ subscr->tmsi = ntohl(tmsi);
+ subscr->tmsi_valid = 1;
+ LOGP(DMM, LOGL_INFO, "got TMSI 0x%08x (%u)\n",
+ subscr->tmsi, subscr->tmsi);
+#ifdef TODO
+ sim: store tmsi
+#endif
+ break;
+ case GSM_MI_TYPE_IMSI:
+ LOGP(DMM, LOGL_INFO, "TMSI removed\n");
+ subscr->tmsi_valid = 0;
+#ifdef TODO
+ sim: delete tmsi
+#endif
+ /* send TMSI REALLOCATION COMPLETE */
+ gsm48_mm_tx_tmsi_reall_cpl(ms);
+ break;
+ default:
+ LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown "
+ "MI type %d.\n", mi_type);
+ }
+ }
+
+ /* send message to PLMN search process */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ /* follow on proceed */
+ if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID))
+ LOGP(DMM, LOGL_NOTICE, "follow-on proceed not supported.\n");
+
+ /* start RR release timer */
+ start_mm_t3240(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+
+ return 0;
+}
+
+/* 4.4.4.7 LOCATION UPDATING REJECT is received */
+static int gsm48_mm_rx_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+
+ if (payload_len < 1) {
+ LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING REJECT "
+ "message error.\n");
+ return -EINVAL;
+ }
+
+ /* RA was successfull */
+ mm->lupd_ra_failure = 0;
+
+ /* stop periodic location updating timer */
+ stop_mm_t3212(mm); /* 4.4.2 */
+
+ /* stop location update timer */
+ stop_mm_t3210(mm);
+
+ /* store until RR is released */
+ mm->lupd_rej_cause = *gh->data;
+
+ /* start RR release timer */
+ start_mm_t3240(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_LOC_UPD_REJ, 0);
+
+ return 0;
+}
+
+/* 4.4.4.7 RR is released after location update reject */
+static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *nmsg;
+ struct gsm322_msg *ngm;
+
+ LOGP(DMM, LOGL_INFO, "Loc. upd. rejected (cause %d)\n",
+ mm->lupd_rej_cause);
+
+ /* new status */
+ switch (mm->lupd_rej_cause) {
+ case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR:
+ case GSM48_REJECT_ILLEGAL_MS:
+ case GSM48_REJECT_ILLEGAL_ME:
+ /* reset attempt counter */
+ mm->lupd_attempt = 0;
+
+ /* SIM invalid */
+ subscr->sim_valid = 0;
+
+ // fall through
+ case GSM48_REJECT_PLMN_NOT_ALLOWED:
+ case GSM48_REJECT_LOC_NOT_ALLOWED:
+ case GSM48_REJECT_ROAMING_NOT_ALLOWED:
+ /* TMSI and LAI invalid */
+ subscr->lai_valid = 0;
+ subscr->tmsi_valid = 0;
+
+ /* key is invalid */
+ subscr->key_seq = 7;
+
+ /* update status */
+ new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA);
+#ifdef TODO
+ sim: delete tmsi, lai
+ sim: delete key seq number
+ sim: apply update state
+#endif
+ /* update has finished */
+ mm->lupd_pending = 0;
+ }
+
+ /* send event to PLMN search process */
+ switch(mm->lupd_rej_cause) {
+ case GSM48_REJECT_ROAMING_NOT_ALLOWED:
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA);
+ break;
+ case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR:
+ case GSM48_REJECT_ILLEGAL_MS:
+ case GSM48_REJECT_ILLEGAL_ME:
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_INVALID_SIM);
+ break;
+ default:
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED);
+ }
+ if (!nmsg)
+ return -ENOMEM;
+ ngm = (struct gsm322_msg *)nmsg->data;
+ ngm->reject = mm->lupd_rej_cause;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ /* forbidden list */
+ switch (mm->lupd_rej_cause) {
+ case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR:
+ LOGP(DSUM, LOGL_INFO, "Location update failed (IMSI unknown "
+ "in HLR)\n");
+ break;
+ case GSM48_REJECT_ILLEGAL_MS:
+ LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal MS)\n");
+ break;
+ case GSM48_REJECT_ILLEGAL_ME:
+ LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal ME)\n");
+ break;
+ case GSM48_REJECT_PLMN_NOT_ALLOWED:
+ gsm_subscr_add_forbidden_plmn(subscr, mm->lupd_mcc,
+ mm->lupd_mnc, mm->lupd_rej_cause);
+ LOGP(DSUM, LOGL_INFO, "Location update failed (PLMN not "
+ "allowed)\n");
+ break;
+ case GSM48_REJECT_LOC_NOT_ALLOWED:
+ case GSM48_REJECT_ROAMING_NOT_ALLOWED:
+ gsm322_add_forbidden_la(ms, mm->lupd_mcc, mm->lupd_mnc,
+ mm->lupd_lac, mm->lupd_rej_cause);
+ LOGP(DSUM, LOGL_INFO, "Location update failed (LAI not "
+ "allowed)\n");
+ break;
+ default:
+ /* 4.4.4.9 continue with failure handling */
+ return gsm48_mm_loc_upd_failed(ms, NULL);
+ }
+
+ /* CS proc triggers: return to IDLE, case 13 is also handled there */
+ return 0;
+}
+
+/* 4.2.2 delay a location update */
+static int gsm48_mm_loc_upd_delay_per(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ LOGP(DMM, LOGL_INFO, "Schedule a pending periodic loc. upd.\n");
+ mm->lupd_periodic = 1;
+
+ return 0;
+}
+
+/* delay a location update retry */
+static int gsm48_mm_loc_upd_delay_retry(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ LOGP(DMM, LOGL_INFO, "Schedule a pending periodic loc. upd.\n");
+ mm->lupd_retry = 1;
+
+ return 0;
+}
+
+/* process failues 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;
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ LOGP(DSUM, LOGL_INFO, "Location update failed\n");
+
+ /* stop location update timer, if running */
+ stop_mm_t3210(mm);
+
+ if (subscr->ustate == GSM_SIM_U1_UPDATED
+ && mm->lupd_mcc == subscr->lai_mcc
+ && mm->lupd_mnc == subscr->lai_mnc
+ && mm->lupd_lac == subscr->lai_lac) {
+ if (mm->lupd_attempt < 4) {
+ LOGP(DSUM, LOGL_INFO, "Try location update later\n");
+ LOGP(DMM, LOGL_INFO, "Loc. upd. failed, retry #%d\n",
+ mm->lupd_attempt);
+
+ /* start update retry timer */
+ start_mm_t3211(mm);
+
+ /* CS process will trigger: return to MM IDLE */
+ return 0;
+ } else
+ LOGP(DMM, LOGL_INFO, "Loc. upd. failed too often.\n");
+ }
+
+ /* TMSI and LAI invalid */
+ subscr->lai_valid = 0;
+ subscr->tmsi_valid = 0;
+
+ /* key is invalid */
+ subscr->key_seq = 7;
+
+ /* update status */
+ new_sim_ustate(subscr, GSM_SIM_U2_NOT_UPDATED);
+
+#ifdef TODO
+ sim: delete tmsi, lai
+ sim: delete key seq number
+ sim: set update status
+#endif
+
+ /* start update retry timer (RR connection is released) */
+ if (mm->lupd_attempt < 4) {
+ mm->start_t3211 = 1;
+ LOGP(DSUM, LOGL_INFO, "Try location update later\n");
+ }
+
+ /* CS process will trigger: return to MM IDLE */
+ return 0;
+}
+
+/* abort a location update due to radio failure or release */
+static int gsm48_mm_rel_loc_upd_abort(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
+
+ if (rrh->msg_type == GSM48_RR_REL_IND) {
+ LOGP(DMM, LOGL_INFO, "RR link released after loc. upd.\n");
+
+ /* continue with failure handling */
+ return gsm48_mm_loc_upd_failed(ms, NULL);
+ }
+
+ LOGP(DMM, LOGL_INFO, "Loc. upd. aborted by radio (cause #%d)\n",
+ rrh->cause);
+
+ /* random access failure, but not two successive failures */
+ if (rrh->cause == RR_REL_CAUSE_RA_FAILURE && !mm->lupd_ra_failure) {
+ mm->lupd_ra_failure = 1;
+
+ /* start RA failure timer */
+ start_mm_t3213(mm);
+
+ return 0;
+ }
+
+ /* RA was successfull or sent twice */
+ mm->lupd_ra_failure = 0;
+
+ /* continue with failure handling */
+ return gsm48_mm_loc_upd_failed(ms, NULL);
+}
+
+/* location update has timed out */
+static int gsm48_mm_loc_upd_timeout(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ /* abort RR connection */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *) msgb_put(nmsg, sizeof(*nrrh));
+ nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER;
+ gsm48_rr_downmsg(ms, nmsg);
+
+ /* continue with failure handling */
+ return gsm48_mm_loc_upd_failed(ms, NULL);
+}
+
+/*
+ * process handlers for MM connections
+ */
+
+/* cm reestablish request message from upper layer */
+static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim,
+ uint8_t cause, uint8_t cm_serv)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm_settings *settings = &ms->settings;
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+ struct gsm48_service_request *nsr; /* NOTE: includes MI length */
+ uint8_t *cm2lv;
+ uint8_t buf[11];
+
+ LOGP(DMM, LOGL_INFO, "CM SERVICE REQUEST (cause %d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+ nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr));
+ cm2lv = (uint8_t *)&nsr->classmark;
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_CM_SERV_REQ;
+
+ /* type and key */
+ nsr->cm_service_type = cm_serv;
+ nsr->cipher_key_seq = subscr->key_seq;
+ /* classmark 2 */
+ cm2lv[0] = sizeof(struct gsm48_classmark2);
+ gsm48_rr_enc_cm2(ms, (struct gsm48_classmark2 *)(cm2lv + 1));
+ /* MI */
+ if (!subscr->sim_valid) { /* have no SIM ? */
+ if (settings->emergency_imsi[0])
+ gsm48_generate_mid_from_imsi(buf,
+ settings->emergency_imsi);
+ else
+ gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMEI);
+ } else if (subscr->tmsi_valid) /* have TMSI ? */
+ gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI);
+ else
+ gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI);
+ msgb_put(nmsg, buf[1]); /* length is part of nsr */
+ memcpy(&nsr->mi_len, buf + 1, 1 + buf[1]);
+ /* prio is optional for eMLPP */
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, rr_prim, cause);
+}
+
+/* cm service abort message from upper layer */
+static int gsm48_mm_tx_cm_service_abort(struct osmocom_ms *ms)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *ngh;
+
+ LOGP(DMM, LOGL_INFO, "CM SERVICE ABORT\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh));
+
+ ngh->proto_discr = GSM48_PDISC_MM;
+ ngh->msg_type = GSM48_MT_MM_CM_SERV_ABORT;
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0);
+}
+
+/* cm service acknowledge is received from lower layer */
+static int gsm48_mm_rx_cm_service_acc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+
+ return gsm48_mm_conn_go_dedic(ms);
+}
+
+/* 9.2.6 CM SERVICE REJECT message received */
+static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ uint8_t abort_any = 0;
+ uint8_t reject_cause;
+
+ if (payload_len < 1) {
+ LOGP(DMM, LOGL_NOTICE, "Short read of cm service reject "
+ "message error.\n");
+ return -EINVAL;
+ }
+
+ /* reject cause */
+ reject_cause = *gh->data;
+
+ LOGP(DMM, LOGL_INFO, "CM SERVICE REJECT (cause %d)\n", reject_cause);
+
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ /* selection action on cause value */
+ switch (reject_cause) {
+ case GSM48_REJECT_IMSI_UNKNOWN_IN_VLR:
+ case GSM48_REJECT_ILLEGAL_ME:
+ abort_any = 1;
+
+ /* TMSI and LAI invalid */
+ subscr->lai_valid = 0;
+ subscr->tmsi_valid = 0;
+
+ /* key is invalid */
+ subscr->key_seq = 7;
+
+ /* update status */
+ new_sim_ustate(subscr, GSM_SIM_U2_NOT_UPDATED);
+
+#ifdef TODO
+ sim: delete tmsi, lai
+ sim: delete key seq number
+ sim: set update status
+#endif
+
+ /* change to WAIT_NETWORK_CMD state impied by abort_any == 1 */
+
+ if (reject_cause == GSM48_REJECT_ILLEGAL_ME)
+ subscr->sim_valid = 0;
+
+ break;
+ default:
+ /* state implied by the number of remaining connections */
+ ;
+ }
+
+ /* release MM connection(s) */
+ gsm48_mm_release_mm_conn(ms, abort_any, 16, 0);
+
+ /* state depends on the existance of remaining MM connections */
+ if (llist_empty(&mm->mm_conn))
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+ else
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+
+ return 0;
+}
+
+/* initiate an MM connection 4.5.1.1
+ *
+ * this function is called when:
+ * - no RR connection exists
+ * - an RR connection exists, but this is the first MM connection
+ * - an RR connection exists, and there are already MM connection(s)
+ */
+static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg,
+ int rr_prim)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ int msg_type = mmh->msg_type;
+ int emergency = 0;
+ uint8_t cause = 0, cm_serv = 0, proto = 0;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+ struct gsm48_mm_conn *conn, *conn_found = NULL;
+
+ /* reset loc. upd. counter on CM service request */
+ mm->lupd_attempt = 0;
+
+ /* find if there is already a pending connection */
+ llist_for_each_entry(conn, &mm->mm_conn, list) {
+ if (conn->state == GSM48_MMXX_ST_CONN_PEND) {
+ conn_found = conn;
+ break;
+ }
+ }
+
+ /* if pending connection */
+ if (conn_found) {
+ LOGP(DMM, LOGL_INFO, "Init MM Connection, but already have "
+ "pending MM Connection.\n");
+ cause = 17;
+ reject:
+ nmsg = NULL;
+ switch(msg_type) {
+ case GSM48_MMCC_EST_REQ:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND,
+ mmh->ref, mmh->transaction_id);
+ break;
+ case GSM48_MMSS_EST_REQ:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND,
+ mmh->ref, mmh->transaction_id);
+ break;
+ case GSM48_MMSMS_EST_REQ:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND,
+ mmh->ref, mmh->transaction_id);
+ break;
+ }
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = cause;
+ gsm48_mmxx_upmsg(ms, nmsg);
+
+ return -EBUSY;
+ }
+ /* in case of an emergency setup */
+ if (msg_type == GSM48_MMCC_EST_REQ && mmh->emergency)
+ emergency = 1;
+
+ /* if sim is not updated */
+ if (!emergency && subscr->ustate != GSM_SIM_U1_UPDATED) {
+ LOGP(DMM, LOGL_INFO, "Init MM Connection, but SIM not "
+ "updated.\n");
+ cause = 21;
+ goto reject;
+ }
+
+ /* current MM idle state
+ * (implies MM IDLE state, otherwise this function is not called)
+ */
+ switch (mm->substate) {
+ case GSM48_MM_SST_NORMAL_SERVICE:
+ case GSM48_MM_SST_PLMN_SEARCH_NORMAL:
+ LOGP(DMM, LOGL_INFO, "Init MM Connection.\n");
+ break; /* allow when normal */
+ case GSM48_MM_SST_LOC_UPD_NEEDED:
+ case GSM48_MM_SST_ATTEMPT_UPDATE:
+ /* store mm request if attempting to update */
+ if (!emergency) {
+ LOGP(DMM, LOGL_INFO, "Init MM Connection, but "
+ "attempting to update.\n");
+ cause = 21;
+ goto reject;
+ /* Some day implement delay and start loc upd. */
+ }
+ break;
+ default:
+ /* reject if not emergency */
+ if (!emergency) {
+ LOGP(DMM, LOGL_INFO, "Init MM Connection, not in "
+ "normal state.\n");
+ cause = 21;
+ goto reject;
+ }
+ break;
+ }
+
+ /* set cause, service, proto */
+ switch(msg_type) {
+ case GSM48_MMCC_EST_REQ:
+ if (emergency) {
+ cause = RR_EST_CAUSE_EMERGENCY;
+ cm_serv = GSM48_CMSERV_EMERGENCY;
+ } else {
+ cause = RR_EST_CAUSE_ORIG_TCHF;
+ cm_serv = GSM48_CMSERV_MO_CALL_PACKET;
+ }
+ proto = GSM48_PDISC_CC;
+ break;
+ case GSM48_MMSS_EST_REQ:
+ cause = RR_EST_CAUSE_OTHER_SDCCH;
+ cm_serv = GSM48_CMSERV_SUP_SERV;
+ proto = GSM48_PDISC_NC_SS;
+ break;
+ case GSM48_MMSMS_EST_REQ:
+ cause = RR_EST_CAUSE_OTHER_SDCCH;
+ cm_serv = GSM48_CMSERV_SMS;
+ proto = GSM48_PDISC_SMS;
+ break;
+ }
+
+ /* create MM connection instance */
+ conn = mm_conn_new(mm, proto, mmh->transaction_id, mmh->ref);
+ if (!conn)
+ return -ENOMEM;
+
+ new_conn_state(conn, GSM48_MMXX_ST_CONN_PEND);
+
+ /* send CM SERVICE REQUEST */
+ if (rr_prim)
+ return gsm48_mm_tx_cm_serv_req(ms, rr_prim, cause, cm_serv);
+ else
+ return 0;
+}
+
+/* 4.5.1.1 a) MM connection request triggers RR connection */
+static int gsm48_mm_init_mm_no_rr(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ int rc;
+
+ /* start MM connection by requesting RR connection */
+ rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_EST_REQ);
+ if (rc)
+ return rc;
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_MM_CON, 0);
+
+ return 0;
+}
+
+/* 4.5.1.1 a) RR is esablised during mm connection, wait for CM accepted */
+static int gsm48_mm_est_mm_con(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* 4.5.1.7 if there is no more MM connection */
+ if (llist_empty(&mm->mm_conn)) {
+ LOGP(DMM, LOGL_INFO, "MM Connection, are already gone.\n");
+
+ /* start RR release timer */
+ start_mm_t3240(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+
+ /* send abort */
+ return gsm48_mm_tx_cm_service_abort(ms);
+ }
+
+ /* start MM connection timer */
+ start_mm_t3230(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_OUT_MM_CONN, 0);
+
+ return 0;
+}
+
+/* 4.5.1.1 b) MM connection request on existing RR connection */
+static int gsm48_mm_init_mm_first(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ int rc;
+
+ /* start MM connection by sending data */
+ rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_DATA_REQ);
+ if (rc)
+ return rc;
+
+ /* stop "RR connection release not allowed" timer */
+ stop_mm_t3241(mm);
+
+ /* start MM connection timer */
+ start_mm_t3230(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_OUT_MM_CONN, 0);
+
+ return 0;
+}
+
+/* 4.5.1.1 b) another MM connection request on existing RR connection */
+static int gsm48_mm_init_mm_more(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ int rc;
+
+ /* start MM connection by sending data */
+ rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_DATA_REQ);
+ if (rc)
+ return rc;
+
+ /* start MM connection timer */
+ start_mm_t3230(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_ADD_OUT_MM_CON, 0);
+
+ return 0;
+}
+
+/* 4.5.1.1 b) delay on WAIT FOR NETWORK COMMAND state */
+static int gsm48_mm_init_mm_wait(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* reject */
+ gsm48_mm_init_mm_reject(ms, msg);
+#if 0
+ this requires handling when leaving this state...
+
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ int rc;
+
+ /* just create the MM connection in pending state */
+ rc = gsm48_mm_init_mm(ms, msg, 0);
+ if (rc)
+ return rc;
+
+ /* start MM connection timer */
+ start_mm_t3230(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_ADD_OUT_MM_CON, 0);
+#endif
+
+ return 0;
+}
+
+/* initiate an mm connection other cases */
+static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ int msg_type = mmh->msg_type;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ /* reject */
+ nmsg = NULL;
+ switch(msg_type) {
+ case GSM48_MMCC_EST_REQ:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref,
+ mmh->transaction_id);
+ break;
+ case GSM48_MMSS_EST_REQ:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref,
+ mmh->transaction_id);
+ break;
+ case GSM48_MMSMS_EST_REQ:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref,
+ mmh->transaction_id);
+ break;
+ }
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = 17;
+ gsm48_mmxx_upmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* accepting pending connection, got dedicated mode
+ *
+ * this function is called:
+ * - when ciphering command is received
+ * - when cm service is accepted
+ */
+static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mm_conn *conn, *conn_found = NULL;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ /* the first and only pending connection is the recent requested */
+ llist_for_each_entry(conn, &mm->mm_conn, list) {
+ if (conn->state == GSM48_MMXX_ST_CONN_PEND) {
+ conn_found = conn;
+ break;
+ }
+ }
+
+ /* if no pending connection (anymore) */
+ if (!conn_found) {
+ LOGP(DMM, LOGL_INFO, "No pending MM Connection.\n");
+
+ return 0;
+ }
+
+ new_conn_state(conn, GSM48_MMXX_ST_DEDICATED);
+
+ /* send establishment confirm */
+ nmsg = NULL;
+ switch(conn_found->protocol) {
+ case GSM48_PDISC_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_CNF, conn->ref,
+ conn->transaction_id);
+ break;
+ case GSM48_PDISC_NC_SS:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_EST_CNF, conn->ref,
+ conn->transaction_id);
+ break;
+ case GSM48_PDISC_SMS:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_EST_CNF, conn->ref,
+ conn->transaction_id);
+ break;
+ }
+ if (!nmsg)
+ return -ENOMEM;
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = 17;
+ gsm48_mmxx_upmsg(ms, nmsg);
+
+ return 0;
+}
+
+/* a RR-SYNC-IND is received during MM connection establishment */
+static int gsm48_mm_sync_ind_wait(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ return gsm48_mm_conn_go_dedic(ms);
+}
+
+/* a RR-SYNC-IND is received during MM connection active */
+static int gsm48_mm_sync_ind_active(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mm_conn *conn;
+ struct msgb *nmsg;
+ struct gsm48_mmxx_hdr *nmmh;
+
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ /* broadcast all MMCC connection(s) */
+ llist_for_each_entry(conn, &mm->mm_conn, list) {
+ /* send MMCC-SYNC-IND */
+ nmsg = NULL;
+ switch(conn->protocol) {
+ case GSM48_PDISC_CC:
+ nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_SYNC_IND,
+ conn->ref, conn->transaction_id);
+ break;
+ }
+ if (!nmsg)
+ continue; /* skip if not of CC type */
+ nmmh = (struct gsm48_mmxx_hdr *)nmsg->data;
+ nmmh->cause = 17;
+ /* copy L3 message */
+ nmsg->l3h = msgb_put(nmsg, msgb_l3len(msg));
+ memcpy(nmsg->l3h, msg->l3h, msgb_l3len(msg));
+ gsm48_mmxx_upmsg(ms, nmsg);
+ }
+
+ return 0;
+}
+
+/* 4.5.1.2 RR abort/release is received during MM connection establishment */
+static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data;
+ int cause;
+
+ /* this conversion is not of any standard */
+ switch(rrh->cause) {
+ case RR_REL_CAUSE_NOT_AUTHORIZED:
+ case RR_REL_CAUSE_EMERGENCY_ONLY:
+ case RR_REL_CAUSE_TRY_LATER:
+ cause = 21;
+ break;
+ case RR_REL_CAUSE_NORMAL:
+ cause = 16;
+ break;
+ default:
+ cause = 47;
+ }
+
+ /* stop MM connection timer */
+ stop_mm_t3230(mm);
+
+ /* release all connections */
+ gsm48_mm_release_mm_conn(ms, 1, cause, 1);
+
+ /* no RR connection, so we return to MM IDLE */
+ if (mm->state == GSM48_MM_ST_WAIT_RR_CONN_MM_CON)
+ return gsm48_mm_return_idle(ms, NULL);
+
+ /* CS process will trigger: return to MM IDLE */
+ return 0;
+}
+
+/* 4.5.1.2 timeout is received during MM connection establishment */
+static int gsm48_mm_timeout_mm_con(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ /* release pending connection */
+ gsm48_mm_release_mm_conn(ms, 0, 102, 0);
+
+ /* state depends on the existance of remaining MM connections */
+ if (llist_empty(&mm->mm_conn)) {
+ /* start RR release timer */
+ start_mm_t3240(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+ } else
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+
+ return 0;
+}
+
+/* respond to paging */
+static int gsm48_mm_est(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+
+ return 0;
+}
+
+/* send CM data */
+static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct gsm48_mm_conn *conn;
+ int msg_type = mmh->msg_type;
+
+ /* get connection, if not exist (anymore), release */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (!conn) {
+ LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already "
+ "released) ref=%d, sending MMXX_REL_IND\n", mmh->ref);
+ switch(msg_type & GSM48_MMXX_MASK) {
+ case GSM48_MMCC_CLASS:
+ mmh->msg_type = GSM48_MMCC_REL_IND;
+ break;
+ case GSM48_MMSS_CLASS:
+ mmh->msg_type = GSM48_MMSS_REL_IND;
+ break;
+ case GSM48_MMSMS_CLASS:
+ mmh->msg_type = GSM48_MMSMS_REL_IND;
+ break;
+ }
+ mmh->cause = 31;
+
+ /* mirror message with REL_IND + cause */
+ return gsm48_mmxx_upmsg(ms, msg);
+ }
+
+ /* pull MM header */
+ msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
+
+ /* push RR header and send down */
+ return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, 0);
+}
+
+/* release of MM connection (active state) */
+static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct gsm48_mm_conn *conn;
+
+ /* get connection, if not exist (anymore), release */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (conn)
+ mm_conn_free(conn);
+
+ /* state depends on the existance of remaining MM connections */
+ if (llist_empty(&mm->mm_conn)) {
+ /* start RR release timer */
+ start_mm_t3240(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+ } else
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+
+ return 0;
+}
+
+/* release of MM connection (wait for additional state) */
+static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct gsm48_mm_conn *conn;
+
+ /* get connection, if not exist (anymore), release */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (conn)
+ mm_conn_free(conn);
+
+ return 0;
+}
+
+/* release of MM connection (wait for active state) */
+static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct gsm48_mm_conn *conn;
+
+ /* get connection, if not exist (anymore), release */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (conn)
+ mm_conn_free(conn);
+
+ /* 4.5.1.7 if there is no MM connection during wait for active state */
+ if (llist_empty(&mm->mm_conn)) {
+ LOGP(DMM, LOGL_INFO, "No MM Connection during 'wait for "
+ "active' state.\n");
+
+ /* start RR release timer */
+ start_mm_t3240(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0);
+
+ /* send abort */
+ return gsm48_mm_tx_cm_service_abort(ms);
+ }
+
+ return 0;
+}
+
+/* release of MM connection (wait for RR state) */
+static int gsm48_mm_release_wait_rr(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ struct gsm48_mm_conn *conn;
+
+ /* get connection, if not exist (anymore), release */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (conn)
+ mm_conn_free(conn);
+
+ /* later, if RR connection is established, the CM SERIVE ABORT
+ * message will be sent
+ */
+ return 0;
+}
+
+/* abort RR connection (due to T3240) */
+static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ /* send abort to RR */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *) msgb_put(nmsg, sizeof(*nrrh));
+ nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER;
+ gsm48_rr_downmsg(ms, nmsg);
+
+ /* CS process will trigger: return to MM IDLE / No SIM */
+ return 0;
+}
+
+/*
+ * other processes
+ */
+
+/* RR is released in other states */
+static int gsm48_mm_rel_other(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* CS process will trigger: return to MM IDLE */
+ return 0;
+}
+
+/*
+ * state machines
+ */
+
+/* state trasitions for MMxx-SAP messages from upper layers */
+static struct downstate {
+ uint32_t states;
+ uint32_t substates;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} downstatelist[] = {
+ /* 4.2.2.1 Normal service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ /* 4.2.2.2 Attempt to update / Loc. Upd. needed */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, /* emergency only */
+
+ /* 4.2.2.3 Limited service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ /* 4.2.2.4 No IMSI */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI),
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ /* 4.2.2.5 PLMN search, normal service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ /* 4.2.2.6 PLMN search */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr},
+
+ /* 4.5.1.1 MM Connection (EST) */
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_first},
+
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_first},
+
+ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES,
+ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_first},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_more},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_more},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_more},
+
+ {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES,
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_wait},
+
+ {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES,
+ GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_wait},
+
+ {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES,
+ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_wait},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_reject},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_reject},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_reject},
+
+ /* 4.5.2.1 MM Connection (DATA) */
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMCC_DATA_REQ, gsm48_mm_data},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMSS_DATA_REQ, gsm48_mm_data},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMSMS_DATA_REQ, gsm48_mm_data},
+
+ /* 4.5.2.1 MM Connection (REL) */
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMCC_REL_REQ, gsm48_mm_release_active},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMSS_REL_REQ, gsm48_mm_release_active},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES,
+ GSM48_MMSMS_REL_REQ, gsm48_mm_release_active},
+
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_add},
+
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_add},
+
+ {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES,
+ GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_add},
+
+ {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES,
+ GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_active},
+
+ {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES,
+ GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_active},
+
+ {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES,
+ GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_active},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
+ GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_rr},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
+ GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_rr},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES,
+ GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_rr},
+};
+
+#define DOWNSLLEN \
+ (sizeof(downstatelist) / sizeof(struct downstate))
+
+int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ int msg_type = mmh->msg_type;
+ struct gsm48_mm_conn *conn;
+ int i, rc;
+
+ /* keep up to date with the transaction ID */
+ conn = mm_conn_by_ref(mm, mmh->ref);
+ if (conn)
+ conn->transaction_id = mmh->transaction_id;
+
+ LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state %s\n",
+ ms->name, get_mmxx_name(msg_type),
+ gsm48_mm_state_names[mm->state]);
+ if (mm->state == GSM48_MM_ST_MM_IDLE)
+ LOGP(DMM, LOGL_INFO, "-> substate %s\n",
+ gsm48_mm_substate_names[mm->substate]);
+ LOGP(DMM, LOGL_INFO, "-> callref %d, transaction_id %d\n",
+ mmh->ref, mmh->transaction_id);
+
+ /* Find function for current state and message */
+ for (i = 0; i < DOWNSLLEN; i++)
+ if ((msg_type == downstatelist[i].type)
+ && ((1 << mm->state) & downstatelist[i].states)
+ && ((1 << mm->substate) & downstatelist[i].substates))
+ break;
+ if (i == DOWNSLLEN) {
+ LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = downstatelist[i].rout(ms, msg);
+
+ if (downstatelist[i].rout != gsm48_mm_data)
+ msgb_free(msg);
+
+ return rc;
+}
+
+/* state trasitions for radio ressource messages (lower layer) */
+static struct rrdatastate {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} rrdatastatelist[] = {
+ /* paging */
+ {SBIT(GSM48_MM_ST_MM_IDLE),
+ GSM48_RR_EST_IND, gsm48_mm_est},
+
+ /* imsi detach */
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 */
+ GSM48_RR_EST_CNF, gsm48_mm_imsi_detach_sent},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 (unsuc.) */
+ GSM48_RR_REL_IND, gsm48_mm_imsi_detach_end},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 (lost) */
+ GSM48_RR_ABORT_IND, gsm48_mm_imsi_detach_end},
+
+ /* location update */
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.1 */
+ GSM48_RR_EST_CNF, gsm48_mm_est_loc_upd},
+
+ {SBIT(GSM48_MM_ST_LOC_UPD_INIT) |
+ SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.9 */
+ GSM48_RR_REL_IND, gsm48_mm_rel_loc_upd_abort},
+
+ {SBIT(GSM48_MM_ST_LOC_UPD_INIT) |
+ SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.9 */
+ GSM48_RR_ABORT_IND, gsm48_mm_rel_loc_upd_abort},
+
+ {SBIT(GSM48_MM_ST_LOC_UPD_REJ), /* 4.4.4.7 */
+ GSM48_RR_REL_IND, gsm48_mm_rel_loc_upd_rej},
+
+ {SBIT(GSM48_MM_ST_LOC_UPD_REJ), /* 4.4.4.7 */
+ GSM48_RR_ABORT_IND, gsm48_mm_rel_loc_upd_rej},
+
+ /* MM connection (EST) */
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), /* 4.5.1.1 */
+ GSM48_RR_EST_CNF, gsm48_mm_est_mm_con},
+
+ /* MM connection (DATA) */
+ {ALL_STATES,
+ GSM48_RR_DATA_IND, gsm48_mm_data_ind},
+
+ /* MM connection (SYNC) */
+ {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.1 */
+ GSM48_RR_SYNC_IND, gsm48_mm_sync_ind_wait},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE),
+ GSM48_RR_SYNC_IND, gsm48_mm_sync_ind_active},
+
+ /* MM connection (REL/ABORT) */
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON) |
+ SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.2 */
+ GSM48_RR_REL_IND, gsm48_mm_abort_mm_con},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON) |
+ SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.2 */
+ GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con},
+
+ /* MM connection (REL/ABORT with re-establishment possibility) */
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), /* not supported */
+ GSM48_RR_REL_IND, gsm48_mm_abort_mm_con},
+
+ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* not supported */
+ GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con},
+
+ /* other */
+ {ALL_STATES,
+ GSM48_RR_REL_IND, gsm48_mm_rel_other},
+
+ {ALL_STATES,
+ GSM48_RR_ABORT_IND, gsm48_mm_rel_other},
+};
+
+#define RRDATASLLEN \
+ (sizeof(rrdatastatelist) / sizeof(struct rrdatastate))
+
+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 i, rc;
+
+ LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' from RR in state %s\n",
+ ms->name, get_rr_name(msg_type),
+ gsm48_mm_state_names[mm->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < RRDATASLLEN; i++)
+ if ((msg_type == rrdatastatelist[i].type)
+ && ((1 << mm->state) & rrdatastatelist[i].states))
+ break;
+ if (i == RRDATASLLEN) {
+ LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = rrdatastatelist[i].rout(ms, msg);
+
+ if (rrdatastatelist[i].rout != gsm48_mm_data_ind)
+ msgb_free(msg);
+
+ return rc;
+}
+
+/* state trasitions for mobile managemnt messages (lower layer) */
+static struct mmdatastate {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} mmdatastatelist[] = {
+ {ALL_STATES, /* 4.3.1.2 */
+ GSM48_MT_MM_TMSI_REALL_CMD, gsm48_mm_rx_tmsi_realloc_cmd},
+
+ {ALL_STATES, /* 4.3.2.2 */
+ GSM48_MT_MM_AUTH_REQ, gsm48_mm_rx_auth_req},
+
+ {ALL_STATES, /* 4.3.2.5 */
+ GSM48_MT_MM_AUTH_REJ, gsm48_mm_rx_auth_rej},
+
+ {ALL_STATES, /* 4.3.3.2 */
+ GSM48_MT_MM_ID_REQ, gsm48_mm_rx_id_req},
+
+ {ALL_STATES, /* 4.3.5.2 */
+ GSM48_MT_MM_ABORT, gsm48_mm_rx_abort},
+
+ {ALL_STATES, /* 4.3.6.2 */
+ GSM48_MT_MM_INFO, gsm48_mm_rx_info},
+
+ {SBIT(GSM48_MM_ST_LOC_UPD_INIT), /* 4.4.4.6 */
+ GSM48_MT_MM_LOC_UPD_ACCEPT, gsm48_mm_rx_loc_upd_acc},
+
+ {SBIT(GSM48_MM_ST_LOC_UPD_INIT), /* 4.4.4.7 */
+ GSM48_MT_MM_LOC_UPD_REJECT, gsm48_mm_rx_loc_upd_rej},
+
+ {ALL_STATES, /* 4.5.1.1 */
+ GSM48_MT_MM_CM_SERV_ACC, gsm48_mm_rx_cm_service_acc},
+
+ {ALL_STATES, /* 4.5.1.1 */
+ GSM48_MT_MM_CM_SERV_REJ, gsm48_mm_rx_cm_service_rej},
+};
+
+#define MMDATASLLEN \
+ (sizeof(mmdatastatelist) / sizeof(struct mmdatastate))
+
+static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+ uint8_t msg_type = gh->msg_type & 0xbf;
+ struct gsm48_mmxx_hdr *mmh;
+ int msg_supported = 0; /* determine, if message is supported at all */
+ int rr_prim = -1, rr_est = -1; /* no prim set */
+ uint8_t skip_ind;
+ int i, rc;
+
+ /* 9.2.19 */
+ if (msg_type == GSM48_MT_MM_NULL)
+ return 0;
+
+ /* pull the RR header */
+ msgb_pull(msg, sizeof(struct gsm48_rr_hdr));
+
+ /* create transaction (if not exists) and push message */
+ switch (pdisc) {
+ case GSM48_PDISC_CC:
+ rr_prim = GSM48_MMCC_DATA_IND;
+ rr_est = GSM48_MMCC_EST_IND;
+ break;
+#if 0
+ case GSM48_PDISC_NC_SS:
+ rr_prim = GSM48_MMSS_DATA_IND;
+ rr_est = GSM48_MMSS_EST_IND;
+ break;
+ case GSM48_PDISC_SMS:
+ rr_prim = GSM48_MMSMS_DATA_IND;
+ rr_est = GSM48_MMSMS_EST_IND;
+ break;
+#endif
+ }
+ if (rr_prim != -1) {
+ uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
+ /* flip */
+ struct gsm48_mm_conn *conn;
+
+ /* find transaction, if any */
+ conn = mm_conn_by_id(mm, pdisc, transaction_id);
+
+ /* create MM connection instance */
+ if (!conn) {
+ conn = mm_conn_new(mm, pdisc, transaction_id,
+ mm_conn_new_ref++);
+ rr_prim = rr_est;
+ }
+ if (!conn)
+ return -ENOMEM;
+
+ /* push new header */
+ msgb_push(msg, sizeof(struct gsm48_mmxx_hdr));
+ mmh = (struct gsm48_mmxx_hdr *)msg->data;
+ mmh->msg_type = rr_prim;
+ mmh->ref = conn->ref;
+
+ /* go MM CONN ACTIVE state */
+ if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD
+ || mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) {
+ /* stop RR release timer */
+ stop_mm_t3240(mm);
+
+ /* stop "RR connection release not allowed" timer */
+ stop_mm_t3241(mm);
+
+ new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0);
+ }
+ }
+
+ /* forward message */
+ switch (pdisc) {
+ case GSM48_PDISC_MM:
+ skip_ind = (gh->proto_discr & 0xf0) >> 4;
+
+ /* ignore if skip indicator is not B'0000' */
+ if (skip_ind)
+ return 0;
+ break; /* follow the selection proceedure below */
+
+ case GSM48_PDISC_CC:
+ return gsm48_rcv_cc(ms, msg);
+
+#if 0
+ case GSM48_PDISC_NC_SS:
+ return gsm48_rcv_ss(ms, msg);
+
+ case GSM48_PDISC_SMS:
+ return gsm48_rcv_sms(ms, msg);
+#endif
+
+ default:
+ LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n",
+ pdisc);
+ msgb_free(msg);
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
+ }
+
+ LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' in MM state %s\n", ms->name,
+ get_mm_name(msg_type), gsm48_mm_state_names[mm->state]);
+
+ stop_mm_t3212(mm); /* 4.4.2 */
+
+ /* 11.2 re-start pending RR release timer */
+ if (bsc_timer_pending(&mm->t3240)) {
+ stop_mm_t3240(mm);
+ start_mm_t3240(mm);
+ }
+
+ /* find function for current state and message */
+ for (i = 0; i < MMDATASLLEN; i++) {
+ if (msg_type == mmdatastatelist[i].type)
+ msg_supported = 1;
+ if ((msg_type == mmdatastatelist[i].type)
+ && ((1 << mm->state) & mmdatastatelist[i].states))
+ break;
+ }
+ if (i == MMDATASLLEN) {
+ msgb_free(msg);
+ if (msg_supported) {
+ LOGP(DMM, LOGL_NOTICE, "Message unhandled at this "
+ "state.\n");
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE);
+ } else {
+ LOGP(DMM, LOGL_NOTICE, "Message not supported.\n");
+ return gsm48_mm_tx_mm_status(ms,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED);
+ }
+ }
+
+ rc = mmdatastatelist[i].rout(ms, msg);
+
+ msgb_free(msg);
+
+ return rc;
+}
+
+/* state trasitions for mobile management events */
+static struct eventstate {
+ uint32_t states;
+ uint32_t substates;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} eventstatelist[] = {
+ /* 4.2.3 return to MM IDLE */
+ {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES,
+ GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found},
+
+ {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES,
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_return_idle},
+
+ /* 4.2.2.1 Normal service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_periodic},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start},
+
+ /* 4.2.2.2 Attempt to update / Loc. upd. needed */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) |
+ SBIT(GSM48_MM_SST_LOC_UPD_NEEDED),
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE),
+ GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE),
+ GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE),
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_periodic},
+
+ /* 4.2.2.3 Limited service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* if allow. */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE),
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */
+
+ /* 4.2.2.4 No IMSI */
+ /* 4.2.2.5 PLMN search, normal service */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, /* 4.2.1.1 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd_delay_retry},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_delay_retry},
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL),
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start},
+
+ /* 4.2.2.6 PLMN search */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, /* 4.2.1.1 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH),
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */
+
+ /* No cell available */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_CELL_AVAIL),
+ GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */
+
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_CELL_AVAIL),
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */
+
+ /* IMSI detach in other cases */
+ {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, /* silently detach */
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_end},
+
+ {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) |
+ SBIT(GSM48_MM_ST_PROCESS_CM_SERV_P) |
+ SBIT(GSM48_MM_ST_WAIT_REEST) |
+ SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON) |
+ SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) |
+ SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, /* we can release */
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_release},
+
+ {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D) |
+ SBIT(GSM48_MM_ST_IMSI_DETACH_INIT) |
+ SBIT(GSM48_MM_ST_IMSI_DETACH_PEND), ALL_STATES, /* ignore */
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_ignore},
+
+ {ALL_STATES, ALL_STATES,
+ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_delay},
+
+ {GSM48_MM_ST_IMSI_DETACH_INIT, ALL_STATES,
+ GSM48_MM_EVENT_TIMEOUT_T3220, gsm48_mm_imsi_detach_end},
+
+ /* location update in other cases */
+ {ALL_STATES, ALL_STATES,
+ GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_ignore},
+
+ {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES,
+ GSM48_MM_EVENT_TIMEOUT_T3210, gsm48_mm_loc_upd_timeout},
+
+ {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES,
+ GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_failed},
+ /* 4.4.4.9 c) (but without retry) */
+
+ /* SYSINFO event */
+ {ALL_STATES, ALL_STATES,
+ GSM48_MM_EVENT_SYSINFO, gsm48_mm_sysinfo},
+
+ /* T3240 timed out */
+ {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD) |
+ SBIT(GSM48_MM_ST_LOC_UPD_REJ), ALL_STATES, /* 4.4.4.8 */
+ GSM48_MM_EVENT_TIMEOUT_T3240, gsm48_mm_abort_rr},
+
+ /* T3230 timed out */
+ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE),
+ GSM48_MM_EVENT_TIMEOUT_T3230, gsm48_mm_timeout_mm_con},
+
+ /* SIM reports SRES */
+ {ALL_STATES, ALL_STATES, /* 4.3.2.2 */
+ GSM48_MM_EVENT_AUTH_RESPONSE, gsm48_mm_tx_auth_rsp},
+
+#if 0
+ /* change in classmark is reported */
+ {ALL_STATES, ALL_STATES,
+ GSM48_MM_EVENT_CLASSMARK_CHG, gsm48_mm_classm_chg},
+#endif
+};
+
+#define EVENTSLLEN \
+ (sizeof(eventstatelist) / sizeof(struct eventstate))
+
+static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ int i, rc;
+
+ if (mm->state == GSM48_MM_ST_MM_IDLE)
+ LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state "
+ "MM IDLE, %s\n", ms->name, get_mmevent_name(msg_type),
+ gsm48_mm_substate_names[mm->substate]);
+ else
+ LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state "
+ "%s\n", ms->name, get_mmevent_name(msg_type),
+ gsm48_mm_state_names[mm->state]);
+
+ /* Find function for current state and message */
+ for (i = 0; i < EVENTSLLEN; i++)
+ if ((msg_type == eventstatelist[i].type)
+ && ((1 << mm->state) & eventstatelist[i].states)
+ && ((1 << mm->substate) & eventstatelist[i].substates))
+ break;
+ if (i == EVENTSLLEN) {
+ LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n");
+ return 0;
+ }
+
+ rc = eventstatelist[i].rout(ms, msg);
+
+ return rc;
+}
+
+/*
+ * MM Register (SIM insert and remove)
+ */
+
+/* register new SIM card and trigger attach */
+static int gsm48_mmr_reg_req(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct msgb *nmsg;
+
+ /* schedule insertion of SIM */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_INSERT);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ /* 4.2.1.2 SIM is inserted in state NO IMSI */
+ if (mm->state == GSM48_MM_ST_MM_IDLE
+ && mm->substate == GSM48_MM_SST_NO_IMSI)
+ new_mm_state(mm, GSM48_MM_ST_MM_IDLE,
+ gsm48_mm_set_plmn_search(ms));
+
+ return 0;
+}
+
+/* trigger detach of sim card */
+static int gsm48_mmr_nreg_req(struct osmocom_ms *ms)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+ struct msgb *nmsg;
+
+ nmsg = gsm322_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(mm->ms, nmsg);
+
+ return 0;
+}
+
+static int gsm48_rcv_mmr(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmr *mmr = (struct gsm48_mmr *)msg->data;
+ int msg_type = mmr->msg_type;
+ int rc = 0;
+
+ LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event\n", ms->name,
+ get_mmr_name(msg_type));
+ switch(msg_type) {
+ case GSM48_MMR_REG_REQ:
+ rc = gsm48_mmr_reg_req(ms);
+ break;
+ case GSM48_MMR_NREG_REQ:
+ rc = gsm48_mmr_nreg_req(ms);
+ break;
+ default:
+ LOGP(DMM, LOGL_NOTICE, "Message unhandled.\n");
+ }
+
+ return rc;
+}
+
+
diff --git a/src/host/layer23/src/gsm48_rr.c b/src/host/layer23/src/gsm48_rr.c
new file mode 100644
index 00000000..4a189f34
--- /dev/null
+++ b/src/host/layer23/src/gsm48_rr.c
@@ -0,0 +1,4232 @@
+#warning rr on MDL error handling (as specified in 04.08 / 04.06)
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/* Very short description of some of the procedures:
+ *
+ * A radio ressource request causes sendig a channel request on RACH.
+ * After receiving of an immediate assignment the link will be establised.
+ * After the link is established, the dedicated mode is entered and confirmed.
+ *
+ * A Paging request also triggers the channel request as above...
+ * After the link is established, the dedicated mode is entered and indicated.
+ *
+ * During dedicated mode, messages are transferred.
+ *
+ * When an assignment command or a handover command is received, the current
+ * link is released. After release, the new channel is activated and the
+ * link is established again. After link is establised, pending messages from
+ * radio ressource are sent.
+ *
+ * When the assignment or handover fails, the old channel is activate and the
+ * link is established again. Also pending messages are sent.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/utils.h>
+#include <osmocore/rsl.h>
+#include <osmocore/gsm48.h>
+#include <osmocore/bitvec.h>
+
+#include <osmocom/osmocom_data.h>
+#include <osmocom/l1l2_interface.h>
+#include <osmocom/logging.h>
+#include <osmocom/networks.h>
+#include <osmocom/l1ctl.h>
+
+static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg);
+static int gsm48_rr_dl_est(struct osmocom_ms *ms);
+
+/*
+ * support
+ */
+
+#define MIN(a, b) ((a < b) ? a : b)
+
+int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
+ uint16_t *mnc, uint16_t *lac)
+{
+ *mcc = ((lai->digits[0] & 0x0f) << 8)
+ | (lai->digits[0] & 0xf0)
+ | (lai->digits[1] & 0x0f);
+ *mnc = ((lai->digits[2] & 0x0f) << 8)
+ | (lai->digits[2] & 0xf0)
+ | ((lai->digits[1] & 0xf0) >> 4);
+ *lac = ntohs(lai->lac);
+
+ return 0;
+}
+
+static int gsm48_encode_chan_h0(struct gsm48_chan_desc *cd, uint8_t tsc,
+ uint16_t arfcn)
+{
+ cd->h0.tsc = tsc;
+ cd->h0.h = 0;
+ cd->h0.arfcn_low = arfcn & 0xff;
+ cd->h0.arfcn_high = arfcn >> 8;
+
+ return 0;
+}
+
+static int gsm48_encode_chan_h1(struct gsm48_chan_desc *cd, uint8_t tsc,
+ uint8_t maio, uint8_t hsn)
+{
+ cd->h1.tsc = tsc;
+ cd->h1.h = 1;
+ cd->h1.maio_low = maio & 0x03;
+ cd->h1.maio_high = maio >> 2;
+ cd->h1.hsn = hsn;
+
+ return 0;
+}
+
+
+static int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc,
+ uint16_t *arfcn)
+{
+ *tsc = cd->h0.tsc;
+ *arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8);
+
+ return 0;
+}
+
+static int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc,
+ uint8_t *maio, uint8_t *hsn)
+{
+ *tsc = cd->h1.tsc;
+ *maio = cd->h1.maio_low | (cd->h1.maio_high << 2);
+ *hsn = cd->h1.hsn;
+
+ return 0;
+}
+
+/* 10.5.2.38 decode Starting time IE */
+static int gsm48_decode_start_time(struct gsm48_rr_cd *cd,
+ struct gsm48_start_time *st)
+{
+ cd->start_t1 = st->t1;
+ cd->start_t2 = st->t2;
+ cd->start_t3 = (st->t3_high << 3) | st->t3_low;
+
+ return 0;
+}
+
+/* decode "BA Range" (10.5.2.1a) */
+static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len,
+ uint32_t *range, uint8_t *ranges, int max_ranges)
+{
+ /* ba = pointer to IE without IE type and length octets
+ * ba_len = number of octets
+ * range = pointer to store decoded range
+ * ranges = number of ranges decoded
+ * max_ranges = maximum number of decoded ranges that can be stored
+ */
+ uint16_t lower, higher;
+ int i, n, required_octets;
+
+ /* find out how much ba ranges will be decoded */
+ n = *ba++;
+ ba_len --;
+ required_octets = 5 * (n >> 1) + 3 * (n & 1);
+ if (required_octets > ba_len) {
+ LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges "
+ "require %d octets, but only %d octets remain.\n",
+ n, required_octets, ba_len);
+ *ranges = 0;
+ return -EINVAL;
+ }
+ if (max_ranges > n)
+ LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number "
+ "of ranges supported by this mobile (%d).\n",
+ n, max_ranges);
+ n = max_ranges;
+
+ /* decode ranges */
+ for (i = 0; i < n; i++) {
+ if (!(i & 1)) {
+ /* decode even range number */
+ lower = *ba++ << 2;
+ lower |= (*ba >> 6);
+ higher = (*ba++ & 0x3f) << 4;
+ higher |= *ba >> 4;
+ } else {
+ lower = (*ba++ & 0x0f) << 6;
+ lower |= *ba >> 2;
+ higher = (*ba++ & 0x03) << 8;
+ higher |= *ba++;
+ /* decode odd range number */
+ }
+ *range++ = (higher << 16) | lower;
+ }
+ *ranges = n;
+
+ return 0;
+}
+
+/*
+ * state transition
+ */
+
+const char *gsm48_rr_state_names[] = {
+ "idle",
+ "connection pending",
+ "dedicated",
+ "release pending",
+};
+
+static void new_rr_state(struct gsm48_rrlayer *rr, int state)
+{
+ if (state < 0 || state >=
+ (sizeof(gsm48_rr_state_names) / sizeof(char *)))
+ return;
+
+ LOGP(DRR, LOGL_INFO, "new state %s -> %s\n",
+ gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]);
+
+ rr->state = state;
+
+ if (state == GSM48_RR_ST_IDLE) {
+ struct msgb *msg, *nmsg;
+
+ /* release dedicated mode, if any */
+// tx_ph_dm_rel_req(rr->ms);
+ l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL);
+ /* free establish message, if any */
+ rr->rr_est_req = 0;
+ if (rr->rr_est_msg) {
+ msgb_free(rr->rr_est_msg);
+ rr->rr_est_msg = NULL;
+ }
+ /* free all pending messages */
+ while((msg = msgb_dequeue(&rr->downqueue)))
+ msgb_free(msg);
+ /* clear all descriptions of last channel */
+ memset(&rr->cd_now, 0, sizeof(rr->cd_now));
+ /* reset cipering */
+ rr->cipher_on = 0;
+ /* tell cell selection process to return to idle mode
+ * NOTE: this must be sent unbuffered, because it will
+ * leave camping state, so it locks against subsequent
+ * establishment of dedicated channel, before the
+ * cell selection process returned to camping state
+ * again. (after cell reselection)
+ */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_RET_IDLE);
+ if (!nmsg)
+ return;
+ gsm322_c_event(rr->ms, nmsg);
+ msgb_free(nmsg);
+ /* reset any BA range */
+ rr->ba_ranges = 0;
+ }
+}
+
+/*
+ * messages
+ */
+
+/* names of RR-SAP */
+static const struct value_string gsm48_rr_msg_names[] = {
+ { GSM48_RR_EST_REQ, "RR_EST_REQ" },
+ { GSM48_RR_EST_IND, "RR_EST_IND" },
+ { GSM48_RR_EST_CNF, "RR_EST_CNF" },
+ { GSM48_RR_REL_IND, "RR_REL_IND" },
+ { GSM48_RR_SYNC_IND, "RR_SYNC_IND" },
+ { GSM48_RR_DATA_REQ, "RR_DATA_REQ" },
+ { GSM48_RR_DATA_IND, "RR_DATA_IND" },
+ { GSM48_RR_UNIT_DATA_IND, "RR_UNIT_DATA_IND" },
+ { GSM48_RR_ABORT_REQ, "RR_ABORT_REQ" },
+ { GSM48_RR_ABORT_IND, "RR_ABORT_IND" },
+ { GSM48_RR_ACT_REQ, "RR_ACT_REQ" },
+ { 0, NULL }
+};
+
+const char *get_rr_name(int value)
+{
+ return get_value_string(gsm48_rr_msg_names, value);
+}
+
+/* allocate GSM 04.08 layer 3 message */
+struct msgb *gsm48_l3_msgb_alloc(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(L3_ALLOC_SIZE+L3_ALLOC_HEADROOM,
+ L3_ALLOC_HEADROOM, "GSM 04.08 L3");
+ if (!msg)
+ return NULL;
+ msg->l3h = msg->data;
+
+ return msg;
+}
+
+/* allocate GSM 04.08 message (RR-SAP) */
+struct msgb *gsm48_rr_msgb_alloc(int msg_type)
+{
+ struct msgb *msg;
+ struct gsm48_rr_hdr *rrh;
+
+ msg = msgb_alloc_headroom(RR_ALLOC_SIZE+RR_ALLOC_HEADROOM,
+ RR_ALLOC_HEADROOM, "GSM 04.08 RR");
+ if (!msg)
+ return NULL;
+
+ rrh = (struct gsm48_rr_hdr *) msgb_put(msg, sizeof(*rrh));
+ rrh->msg_type = msg_type;
+
+ return msg;
+}
+
+/* queue message (RR-SAP) */
+int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_mmlayer *mm = &ms->mmlayer;
+
+ msgb_enqueue(&mm->rr_upqueue, msg);
+
+ return 0;
+}
+
+/* push rsl header and send (RSL-SAP) */
+static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type,
+ struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ if (!msg->l3h) {
+ printf("FIX l3h\n");
+ exit (0);
+ }
+ rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr,
+ rr->cd_now.link_id, 1);
+
+ return rslms_recvmsg(msg, ms);
+}
+
+/* enqueue messages (RSL-SAP) */
+static int gsm48_rx_rll(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ msgb_enqueue(&rr->rsl_upqueue, msg);
+
+ return 0;
+}
+
+/* input function that L2 calls when sending messages up to L3 */
+static int gsm48_rx_rsl(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = gsm48_rx_rll(msg, ms);
+ break;
+ default:
+ /* FIXME: implement this */
+ LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n",
+ rslh->msg_discr);
+ msgb_free(msg);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/* dequeue messages (RSL-SAP) */
+int gsm48_rsl_dequeue(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *msg;
+ int work = 0;
+
+ while ((msg = msgb_dequeue(&rr->rsl_upqueue))) {
+ /* msg is freed there */
+ gsm48_rcv_rsl(ms, msg);
+ work = 1; /* work done */
+ }
+
+ return work;
+}
+
+/*
+ * timers handling
+ */
+
+/* special timer to ensure that UA is sent before disconnecting channel */
+static void timeout_rr_t_rel_wait(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+
+ LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n");
+
+ /* return to idle now */
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+}
+
+/* 3.4.13.1.1: Timeout of T3110 */
+static void timeout_rr_t3110(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+ struct osmocom_ms *ms = rr->ms;
+ struct msgb *nmsg;
+ uint8_t *mode;
+
+ LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n");
+
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* disconnect the main signalling link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = 1; /* local release */
+ gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+
+ return;
+}
+
+static void timeout_rr_t3122(void *arg)
+{
+ LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n");
+}
+
+static void timeout_rr_t3126(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+ struct osmocom_ms *ms = rr->ms;
+
+ LOGP(DRR, LOGL_INFO, "timer T3126 has fired\n");
+ if (rr->rr_est_req) {
+ struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ struct gsm48_rr_hdr *rrh;
+
+ LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
+ if (!msg)
+ return;
+ rrh = (struct gsm48_rr_hdr *)msg->data;
+ rrh->cause = RR_REL_CAUSE_RA_FAILURE;
+ gsm48_rr_upmsg(ms, msg);
+ }
+
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+}
+
+static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d seconds\n", sec);
+ rr->t_rel_wait.cb = timeout_rr_t_rel_wait;
+ rr->t_rel_wait.data = rr;
+ bsc_schedule_timer(&rr->t_rel_wait, sec, micro);
+}
+
+static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T3110 with %d seconds\n", sec);
+ rr->t3110.cb = timeout_rr_t3110;
+ rr->t3110.data = rr;
+ bsc_schedule_timer(&rr->t3110, sec, micro);
+}
+
+static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T3122 with %d seconds\n", sec);
+ rr->t3122.cb = timeout_rr_t3122;
+ rr->t3122.data = rr;
+ bsc_schedule_timer(&rr->t3122, sec, micro);
+}
+
+static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro)
+{
+ LOGP(DRR, LOGL_INFO, "starting T3126 with %d seconds\n", sec);
+ rr->t3126.cb = timeout_rr_t3126;
+ rr->t3126.data = rr;
+ bsc_schedule_timer(&rr->t3126, sec, micro);
+}
+
+static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr)
+{
+ if (bsc_timer_pending(&rr->t_rel_wait)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n");
+ bsc_del_timer(&rr->t_rel_wait);
+ }
+}
+
+static void stop_rr_t3110(struct gsm48_rrlayer *rr)
+{
+ if (bsc_timer_pending(&rr->t3110)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n");
+ bsc_del_timer(&rr->t3110);
+ }
+}
+
+static void stop_rr_t3122(struct gsm48_rrlayer *rr)
+{
+ if (bsc_timer_pending(&rr->t3122)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T3122\n");
+ bsc_del_timer(&rr->t3122);
+ }
+}
+
+static void stop_rr_t3126(struct gsm48_rrlayer *rr)
+{
+ if (bsc_timer_pending(&rr->t3126)) {
+ LOGP(DRR, LOGL_INFO, "stopping pending timer T3126\n");
+ bsc_del_timer(&rr->t3126);
+ }
+}
+
+/*
+ * status
+ */
+
+/* send rr status request */
+static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_rr_status *st;
+
+ LOGP(DRR, LOGL_INFO, "RR STATUS (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ 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;
+
+ /* rr cause */
+ st->rr_cause = cause;
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/*
+ * ciphering
+ */
+
+/* send chiperhing mode complete */
+static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr)
+{
+ struct gsm_settings *set = &ms->settings;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ uint8_t buf[11], *tlv;
+
+ LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL;
+
+ /* MI */
+ if (cr) {
+ gsm48_generate_mid_from_imsi(buf, set->imeisv);
+ tlv = msgb_put(nmsg, 2 + buf[1]);
+ memcpy(tlv, buf, 2 + buf[1]);
+ }
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* receive ciphering mode command */
+static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm_support *sup = &ms->support;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm);
+ uint8_t sc, alg_id, cr;
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of CIPHERING MODE COMMAND "
+ "message.\n");
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+
+ /* cipher mode setting */
+ sc = cm->sc;
+ alg_id = cm->alg_id;
+ /* cipher mode response */
+ cr = cm->cr;
+
+ if (sc)
+ LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)",
+ sc, cr);
+ else
+ LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, "
+ "algo=A5/%d cr=%u)", sc, alg_id + 1, cr);
+
+ /* 3.4.7.2 */
+ if (rr->cipher_on && sc) {
+ LOGP(DRR, LOGL_INFO, "cipering already applied.\n");
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+
+ /* check if we actually support this cipher */
+ if ((alg_id == GSM_CIPHER_A5_1 && !sup->a5_1)
+ || (alg_id == GSM_CIPHER_A5_2 && !sup->a5_2)
+ || (alg_id == GSM_CIPHER_A5_3 && !sup->a5_3)
+ || (alg_id == GSM_CIPHER_A5_4 && !sup->a5_4)
+ || (alg_id == GSM_CIPHER_A5_5 && !sup->a5_5)
+ || (alg_id == GSM_CIPHER_A5_6 && !sup->a5_6)
+ || (alg_id == GSM_CIPHER_A5_7 && !sup->a5_7))
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_CHAN_MODE_UNACCT);
+
+ /* change to ciphering */
+#ifdef TODO
+ rsl command to activate ciperhing
+#endif
+ rr->cipher_on = sc, rr->cipher_type = alg_id;
+
+ /* response */
+ return gsm48_rr_tx_cip_mode_cpl(ms, cr);
+}
+
+/*
+ * classmark
+ */
+
+/* Encode "Classmark 3" (10.5.1.7) */
+static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len)
+{
+ struct gsm_support *sup = &ms->support;
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = buf;
+ bv.data_len = 12;
+
+ /* spare bit */
+ bitvec_set_bit(&bv, 0);
+ /* band 3 supported */
+ if (sup->dcs_1800)
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ /* band 2 supported */
+ if (sup->e_gsm || sup->r_gsm)
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ /* band 1 supported */
+ if (sup->p_gsm && !(sup->e_gsm || sup->r_gsm))
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ /* a5 bits */
+ if (sup->a5_7)
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ if (sup->a5_6)
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ if (sup->a5_5)
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ if (sup->a5_4)
+ bitvec_set_bit(&bv, ONE);
+ else
+ bitvec_set_bit(&bv, ZERO);
+ /* radio capability */
+ if (sup->dcs_1800 && !sup->p_gsm && !(sup->e_gsm || sup->r_gsm)) {
+ /* dcs only */
+ bitvec_set_uint(&bv, 0, 4);
+ bitvec_set_uint(&bv, sup->dcs_capa, 4);
+ } else
+ if (sup->dcs_1800 && (sup->p_gsm || (sup->e_gsm || sup->r_gsm))) {
+ /* dcs */
+ bitvec_set_uint(&bv, sup->dcs_capa, 4);
+ /* low band */
+ bitvec_set_uint(&bv, sup->low_capa, 4);
+ } else {
+ /* low band only */
+ bitvec_set_uint(&bv, 0, 4);
+ bitvec_set_uint(&bv, sup->low_capa, 4);
+ }
+ /* r support */
+ if (sup->r_gsm) {
+ bitvec_set_bit(&bv, ONE);
+ bitvec_set_uint(&bv, sup->r_capa, 3);
+ } else {
+ bitvec_set_bit(&bv, ZERO);
+ }
+ /* multi slot support */
+ if (sup->ms_sup) {
+ bitvec_set_bit(&bv, ONE);
+ bitvec_set_uint(&bv, sup->ms_sup, 5);
+ } else {
+ bitvec_set_bit(&bv, ZERO);
+ }
+ /* ucs2 treatment */
+ if (sup->ucs2_treat) {
+ bitvec_set_bit(&bv, ONE);
+ } else {
+ bitvec_set_bit(&bv, ZERO);
+ }
+ /* support extended measurements */
+ if (sup->ext_meas) {
+ bitvec_set_bit(&bv, ONE);
+ } else {
+ bitvec_set_bit(&bv, ZERO);
+ }
+ /* support measurement capability */
+ if (sup->meas_cap) {
+ bitvec_set_bit(&bv, ONE);
+ bitvec_set_uint(&bv, sup->sms_val, 4);
+ bitvec_set_uint(&bv, sup->sm_val, 4);
+ } else {
+ bitvec_set_bit(&bv, ZERO);
+ }
+ /* positioning method capability */
+ if (sup->loc_serv) {
+ bitvec_set_bit(&bv, ONE);
+ bitvec_set_bit(&bv, sup->e_otd_ass == 1);
+ bitvec_set_bit(&bv, sup->e_otd_based == 1);
+ bitvec_set_bit(&bv, sup->gps_ass == 1);
+ bitvec_set_bit(&bv, sup->gps_based == 1);
+ bitvec_set_bit(&bv, sup->gps_conv == 1);
+ } else {
+ bitvec_set_bit(&bv, ZERO);
+ }
+
+ /* partitial bytes will be completed */
+ *len = (bv.cur_bit + 7) >> 3;
+ bitvec_spare_padding(&bv, (*len * 8) - 1);
+
+ return 0;
+}
+
+/* encode classmark 2 */
+int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm_support *sup = &ms->support;
+
+ if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885)
+ cm->pwr_lev = sup->pwr_lev_1800;
+ else
+ cm->pwr_lev = sup->pwr_lev_900;
+ cm->a5_1 = sup->a5_1;
+ cm->es_ind = sup->es_ind;
+ cm->rev_lev = sup->rev_lev;
+ cm->fc = (sup->r_gsm || sup->e_gsm);
+ cm->vgcs = sup->vgcs;
+ cm->vbs = sup->vbs;
+ cm->sm_cap = sup->sms_ptp;
+ cm->ss_scr = sup->ss_ind;
+ cm->ps_cap = sup->ps_cap;
+ cm->a5_2 = sup->a5_2;
+ cm->a5_3 = sup->a5_3;
+ cm->cmsp = sup->cmsp;
+ cm->solsa = sup->solsa;
+ cm->lcsva_cap = sup->lcsva;
+
+ return 0;
+}
+
+/* send classmark change */
+static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms)
+{
+ struct gsm_support *sup = &ms->support;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_cm_change *cc;
+ uint8_t cm3[14], *tlv;
+
+ LOGP(DRR, LOGL_INFO, "CLASSMARK CHANGE\n");
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ cc = (struct gsm48_cm_change *) msgb_put(nmsg, sizeof(*cc));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_CLSM_CHG;
+
+ /* classmark 2 */
+ cc->cm2_len = sizeof(cc->cm2);
+ gsm48_rr_enc_cm2(ms, &cc->cm2);
+
+ /* classmark 3 */
+ if (sup->dcs_1800 || sup->e_gsm || sup->r_gsm
+ || sup->a5_7 || sup->a5_6 || sup->a5_5 || sup->a5_4
+ || sup->ms_sup
+ || sup->ucs2_treat
+ || sup->ext_meas || sup->meas_cap
+ || sup->loc_serv) {
+ cc->cm2.cm3 = 1;
+ cm3[0] = GSM48_IE_CLASSMARK3;
+ gsm48_rr_enc_cm3(ms, cm3 + 2, &cm3[1]);
+ tlv = msgb_put(nmsg, 2 + cm3[1]);
+ memcpy(tlv, cm3, 2 + cm3[1]);
+ }
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* receiving classmark enquiry */
+static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* send classmark */
+ return gsm48_rr_tx_cm_change(ms);
+}
+
+/*
+ * random access
+ */
+
+/* temporary timer until we have time control over channnel request */
+/* TODO: turn this into a channel activation timeout, later */
+#define RSL_MT_CHAN_CNF 0x19
+#include <osmocom/l1ctl.h>
+int gsm48_rr_rach_conf(struct osmocom_ms *ms, uint32_t fn)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RR");
+ struct abis_rsl_rll_hdr *rllh =
+ (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rllh));
+
+ if (!rr->wait_assign) {
+ LOGP(DRR, LOGL_INFO, "RACH confirm ignored, not waiting for "
+ "assignment.\n");
+ return 0;
+ }
+ LOGP(DRR, LOGL_INFO, "RACH confirm framenr=%u\n", fn);
+ rr->cr_hist[0].valid = 2;
+ rr->cr_hist[0].fn = fn;
+
+ rllh->c.msg_type = RSL_MT_CHAN_CNF;
+ msg->l2h = (unsigned char *)rllh;
+ gsm48_rcv_rsl(ms, msg);
+
+ return 0;
+}
+
+/* start random access */
+static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+ uint8_t chan_req_val, chan_req_mask;
+ int rc;
+
+ LOGP(DSUM, LOGL_INFO, "Establish radio link due to %s request\n",
+ (paging) ? "paging" : "mobility management");
+
+ /* ignore paging, if not camping */
+ if (paging
+ && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && cs->state != GSM322_C7_CAMPED_ANY_CELL))) {
+ LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n");
+ return -EINVAL;
+ }
+
+ /* tell cell selection process to leave idle mode
+ * NOTE: this must be sent unbuffered, because the state may not
+ * change until idle mode is left
+ */
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE);
+ if (!nmsg)
+ return -ENOMEM;
+ rc = gsm322_c_event(ms, nmsg);
+ msgb_free(nmsg);
+ if (rc) {
+ if (paging)
+ return rc;
+ LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n");
+ goto undefined;
+ }
+
+ /* 3.3.1.1.2 */
+ new_rr_state(rr, GSM48_RR_ST_CONN_PEND);
+
+ /* number of retransmissions (with first transmission) */
+ rr->n_chan_req = s->max_retrans + 1;
+
+#warning HACK: always request SDCCH for test
+cause = RR_EST_CAUSE_LOC_UPD;
+ /* generate CHAN REQ (9.1.8) */
+ switch (cause) {
+ case RR_EST_CAUSE_EMERGENCY:
+ /* 101xxxxx */
+ chan_req_mask = 0x1f;
+ chan_req_val = 0xa0;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Emergency call)\n",
+ chan_req_val);
+ break;
+ case RR_EST_CAUSE_REESTAB_TCH_F:
+ chan_req_mask = 0x1f;
+ chan_req_val = 0xc0;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (re-establish "
+ "TCH/F)\n", chan_req_val);
+ break;
+ case RR_EST_CAUSE_REESTAB_TCH_H:
+ if (s->neci) {
+ chan_req_mask = 0x03;
+ chan_req_val = 0x68;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
+ "(re-establish TCH/H with NECI)\n",
+ chan_req_val);
+ } else {
+ chan_req_mask = 0x1f;
+ chan_req_val = 0xc0;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
+ "(re-establish TCH/H no NECI)\n", chan_req_val);
+ }
+ break;
+ case RR_EST_CAUSE_REESTAB_2_TCH_H:
+ if (s->neci) {
+ chan_req_mask = 0x03;
+ chan_req_val = 0x6c;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
+ "(re-establish TCH/H+TCH/H with NECI)\n",
+ chan_req_val);
+ } else {
+ chan_req_mask = 0x1f;
+ chan_req_val = 0xc0;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x "
+ "(re-establish TCH/H+TCH/H no NECI)\n",
+ chan_req_val);
+ }
+ break;
+ case RR_EST_CAUSE_ANS_PAG_ANY:
+ chan_req_mask = 0x1f;
+ chan_req_val = 0x80;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING "
+ "Any channel)\n", chan_req_val);
+ break;
+ case RR_EST_CAUSE_ANS_PAG_SDCCH:
+ chan_req_mask = 0x0f;
+ chan_req_val = 0x10;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n",
+ chan_req_val);
+ break;
+ case RR_EST_CAUSE_ANS_PAG_TCH_F:
+ /* ms supports no dual rate */
+ chan_req_mask = 0x1f;
+ chan_req_val = 0x80;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n",
+ chan_req_val);
+ break;
+ case RR_EST_CAUSE_ANS_PAG_TCH_ANY:
+ /* ms supports no dual rate */
+ chan_req_mask = 0x1f;
+ chan_req_val = 0x80;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/H or "
+ "TCH/F)\n", chan_req_val);
+ break;
+ case RR_EST_CAUSE_ORIG_TCHF:
+ /* ms supports no dual rate */
+ chan_req_mask = 0x1f;
+ chan_req_val = 0xe0;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Orig TCH/F)\n",
+ chan_req_val);
+ break;
+ case RR_EST_CAUSE_LOC_UPD:
+ if (s->neci) {
+ chan_req_mask = 0x0f;
+ chan_req_val = 0x00;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location "
+ "Update with NECI)\n", chan_req_val);
+ } else {
+ chan_req_mask = 0x1f;
+ chan_req_val = 0x00;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location "
+ "Update no NECI)\n", chan_req_val);
+ }
+ break;
+ case RR_EST_CAUSE_OTHER_SDCCH:
+ if (s->neci) {
+ chan_req_mask = 0x0f;
+ chan_req_val = 0x01;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OHTER "
+ "with NECI)\n", chan_req_val);
+ } else {
+ chan_req_mask = 0x1f;
+ chan_req_val = 0xe0;
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER "
+ "no NECI)\n", chan_req_val);
+ }
+ break;
+ default:
+ if (!rr->rr_est_req) /* no request from MM */
+ return -EINVAL;
+
+ LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: with unknown "
+ "establishment cause: %d\n", cause);
+ undefined:
+ LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
+
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = RR_REL_CAUSE_UNDEFINED;
+ gsm48_rr_upmsg(ms, nmsg);
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+ return -EINVAL;
+ }
+
+ /* store value, mask and history */
+ rr->chan_req_val = chan_req_val;
+ rr->chan_req_mask = chan_req_mask;
+ rr->cr_hist[2].valid = 0;
+ rr->cr_hist[1].valid = 0;
+ rr->cr_hist[0].valid = 0;
+
+ /* if channel is already active somehow */
+ if (cs->ccch_state == GSM322_CCCH_ST_DATA)
+ return gsm48_rr_tx_rand_acc(ms, NULL);
+
+ return 0;
+}
+
+/* send first/next channel request in conn pend state */
+int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
+ struct msgb *nmsg;
+ struct l1ctl_info_ul *nul;
+ struct l1ctl_rach_req *nra;
+ int slots;
+ uint8_t chan_req;
+
+ if (cs->ccch_state != GSM322_CCCH_ST_DATA) {
+ LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n");
+
+ if (rr->rr_est_req) {
+ struct msgb *msg =
+ gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ struct gsm48_rr_hdr *rrh;
+
+ LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n");
+ if (!msg)
+ return -ENOMEM;
+ rrh = (struct gsm48_rr_hdr *)msg->data;
+ rrh->cause = RR_REL_CAUSE_RA_FAILURE;
+ gsm48_rr_upmsg(ms, msg);
+ }
+
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+
+ return 0;
+ }
+
+ if (rr->state == GSM48_RR_ST_IDLE) {
+ LOGP(DRR, LOGL_INFO, "MM already released RR.\n");
+
+ return 0;
+ }
+
+ LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (requests left %d)\n",
+ rr->n_chan_req);
+
+ if (!rr->n_chan_req) {
+ LOGP(DRR, LOGL_INFO, "Done with sending RANDOM ACCESS "
+ "bursts\n");
+ if (!bsc_timer_pending(&rr->t3126))
+ start_rr_t3126(rr, 5, 0); /* TODO improve! */
+ return 0;
+ }
+ rr->n_chan_req--;
+
+ if (!rr->wait_assign) {
+ /* first random acces, without delay of slots */
+ slots = 0;
+ rr->wait_assign = 1;
+ } else {
+ /* subsequent random acces, 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 */
+ slots = 55;
+ else
+ slots = 41;
+ break;
+ case 4: case 9: case 16:
+ if (s->ccch_conf != 1)
+ slots = 76;
+ else
+ slots = 52;
+ break;
+ case 5: case 10: case 20:
+ if (s->ccch_conf != 1)
+ slots = 109;
+ else
+ slots = 58;
+ break;
+ case 6: case 11: case 25:
+ if (s->ccch_conf != 1)
+ slots = 163;
+ else
+ slots = 86;
+ break;
+ default:
+ if (s->ccch_conf != 1)
+ slots = 217;
+ else
+ slots = 115;
+ break;
+ }
+ }
+
+ /* resend chan_req with new randiom */
+#ifdef TODO
+ nmsg = gsm48_rsl_msgb_alloc();
+#else
+ nmsg = msgb_alloc_headroom(64, 48, "RAND_ACC");
+ struct l1ctl_hdr *l1h;
+ nmsg->l1h = msgb_put(nmsg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) nmsg->l1h;
+ l1h->msg_type = L1CTL_RACH_REQ;
+ if (!nmsg)
+ return -ENOMEM;
+ nul = (struct l1ctl_info_ul *) msgb_put(nmsg, sizeof(*nul));
+#endif
+ nra = (struct l1ctl_rach_req *) msgb_put(nmsg, sizeof(*nra));
+ chan_req = random();
+ chan_req &= rr->chan_req_mask;
+ chan_req |= rr->chan_req_val;
+ nra->ra = chan_req;
+#warning TODO
+ nra->mf_off = 1; // (random() % s->tx_integer) + slots;
+ nra->fn51 = (s->ccch_conf == 1) ? 27 : 50;
+
+ LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (Tx-integer %d combined %s "
+ "S(lots) %d ra 0x%02x offset %d)\n", s->tx_integer,
+ (s->ccch_conf == 1) ? "yes": "no", slots, nra->ra, nra->mf_off);
+
+ /* shift history and store */
+ memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]),
+ sizeof(struct gsm48_cr_hist));
+ memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]),
+ sizeof(struct gsm48_cr_hist));
+ rr->cr_hist[0].valid = 1;
+ rr->cr_hist[0].chan_req = chan_req;
+
+#ifdef TODO
+ add layer 1 conrols to RSL...
+ return gsm48_send_rsl(ms, RSL_MT_CHAN_REQ, nmsg);
+#else
+//#warning disabled!
+ return osmo_send_l1(ms, nmsg);
+#endif
+}
+
+/*
+ * system information
+ */
+
+/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
+static int gsm48_decode_freq_list(struct gsm_support *sup,
+ struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask,
+ uint8_t frqt)
+{
+ int i;
+
+ /* NOTES:
+ *
+ * The Range format uses "SMOD" computation.
+ * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
+ * A cascade of multiple SMOD computations is simpified:
+ * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
+ *
+ * The Range format uses 16 octets of data in SYSTEM INFORMATION.
+ * When used in dedicated messages, the length can be less.
+ * In this case the ranges are decoded for all frequencies that
+ * fit in the block of given length.
+ */
+
+ /* tabula rasa */
+ for (i = 0; i < 1024; i++)
+ f[i].mask &= ~frqt;
+
+ /* 00..XXX. */
+ if ((cd[0] & 0xc0 & mask) == 0x00) {
+ /* Bit map 0 format */
+ if (len < 16)
+ return -EINVAL;
+ for (i = 1; i <= 124; i++)
+ if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
+ f[i].mask |= frqt;
+
+ return 0;
+ }
+
+ /* only Bit map 0 format for P-GSM */
+ if (sup->p_gsm && !sup->e_gsm && !sup->r_gsm && !sup->dcs_1800)
+ return 0;
+
+ /* 10..0XX. */
+ if ((cd[0] & 0xc8 & mask) == 0x80) {
+ /* Range 1024 format */
+ uint16_t w[17]; /* 1..16 */
+ struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd;
+
+ if (len < 2)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ if (r->f0)
+ f[0].mask |= frqt;
+ w[1] = (r->w1_hi << 8) | r->w1_lo;
+ if (len >= 4)
+ w[2] = (r->w2_hi << 1) | r->w2_lo;
+ if (len >= 5)
+ w[3] = (r->w3_hi << 2) | r->w3_lo;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 2) | r->w4_lo;
+ if (len >= 7)
+ w[5] = (r->w5_hi << 2) | r->w5_lo;
+ if (len >= 8)
+ w[6] = (r->w6_hi << 2) | r->w6_lo;
+ if (len >= 9)
+ w[7] = (r->w7_hi << 2) | r->w7_lo;
+ if (len >= 10)
+ w[8] = (r->w8_hi << 1) | r->w8_lo;
+ if (len >= 10)
+ w[9] = r->w9;
+ if (len >= 11)
+ w[10] = r->w10;
+ if (len >= 12)
+ w[11] = (r->w11_hi << 6) | r->w11_lo;
+ if (len >= 13)
+ w[12] = (r->w12_hi << 5) | r->w12_lo;
+ if (len >= 14)
+ w[13] = (r->w13_hi << 4) | r->w13_lo;
+ if (len >= 15)
+ w[14] = (r->w14_hi << 3) | r->w14_lo;
+ if (len >= 16)
+ w[15] = (r->w15_hi << 2) | r->w15_lo;
+ if (len >= 16)
+ w[16] = r->w16;
+ if (w[1])
+ f[w[1]].mask |= frqt;
+ if (w[2])
+ f[((w[1] - 512 + w[2] - 1) % 1023) + 1].mask |= frqt;
+ if (w[3])
+ f[((w[1] + w[3] - 1) % 1023) + 1].mask |= frqt;
+ if (w[4])
+ f[((w[1] - 512 + ((w[2] - 256 + w[4] - 1) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[5])
+ f[((w[1] + ((w[3] - 256 - w[5] - 1) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[6])
+ f[((w[1] - 512 + ((w[2] + w[6] - 1) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[7])
+ f[((w[1] + ((w[3] + w[7] - 1) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[8])
+ f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + w[8] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[9])
+ f[((w[1] + ((w[3] - 256 + ((w[5] - 128 + w[9] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[10])
+ f[((w[1] - 512 + ((w[2] + ((w[6] - 128 + w[10] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[11])
+ f[((w[1] + ((w[3] + ((w[7] - 128 + w[11] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[12])
+ f[((w[1] - 512 + ((w[2] - 256 + ((w[4] + w[12] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[13])
+ f[((w[1] + ((w[3] - 256 + ((w[5] + w[13] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[14])
+ f[((w[1] - 512 + ((w[2] + ((w[6] + w[14] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[15])
+ f[((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+ if (w[16])
+ f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + ((w[8] - 64 + w[16] - 1) % 127)) % 255)) % 511)) % 1023) + 1].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..100. */
+ if ((cd[0] & 0xce & mask) == 0x88) {
+ /* Range 512 format */
+ uint16_t w[18]; /* 1..17 */
+ struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd;
+
+ if (len < 4)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = (r->w1_hi << 2) | r->w1_lo;
+ if (len >= 5)
+ w[2] = (r->w2_hi << 2) | r->w2_lo;
+ if (len >= 6)
+ w[3] = (r->w3_hi << 2) | r->w3_lo;
+ if (len >= 7)
+ w[4] = (r->w4_hi << 1) | r->w4_lo;
+ if (len >= 7)
+ w[5] = r->w5;
+ if (len >= 8)
+ w[6] = r->w6;
+ if (len >= 9)
+ w[7] = (r->w7_hi << 6) | r->w7_lo;
+ if (len >= 10)
+ w[8] = (r->w8_hi << 4) | r->w8_lo;
+ if (len >= 11)
+ w[9] = (r->w9_hi << 2) | r->w9_lo;
+ if (len >= 11)
+ w[10] = r->w10;
+ if (len >= 12)
+ w[11] = r->w11;
+ if (len >= 13)
+ w[12] = (r->w12_hi << 4) | r->w12_lo;
+ if (len >= 14)
+ w[13] = (r->w13_hi << 2) | r->w13_lo;
+ if (len >= 14)
+ w[14] = r->w14;
+ if (len >= 15)
+ w[15] = r->w15;
+ if (len >= 16)
+ w[16] = (r->w16_hi << 3) | r->w16_lo;
+ if (len >= 16)
+ w[17] = r->w17;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + ((w[1] - 256 + w[2] - 1) % 511) + 1) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + ((w[1] + w[3] - 1) % 511) + 1) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + w[4] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + ((w[1] + ((w[3] - 128 + w[5] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + ((w[1] - 256 + ((w[2] + w[6] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + w[8] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] - 64 + w[9] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + ((w[1] - 256 + ((w[2] + ((w[6] - 64 + w[10] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 64 + w[11] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] + w[12] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] + w[13] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + ((w[1] - 256 + ((w[2] + ((w[6] + w[14] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + ((w[8] - 32 + w[16] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] - 64 + ((w[9] - 32 + w[17] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..101. */
+ if ((cd[0] & 0xce & mask) == 0x8a) {
+ /* Range 256 format */
+ uint16_t w[22]; /* 1..21 */
+ struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd;
+
+ if (len < 4)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = (r->w1_hi << 1) | r->w1_lo;
+ if (len >= 4)
+ w[2] = r->w2;
+ if (len >= 5)
+ w[3] = r->w3;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 5) | r->w4_lo;
+ if (len >= 7)
+ w[5] = (r->w5_hi << 3) | r->w5_lo;
+ if (len >= 8)
+ w[6] = (r->w6_hi << 1) | r->w6_lo;
+ if (len >= 8)
+ w[7] = r->w7;
+ if (len >= 9)
+ w[8] = (r->w8_hi << 4) | r->w8_lo;
+ if (len >= 10)
+ w[9] = (r->w9_hi << 1) | r->w9_lo;
+ if (len >= 10)
+ w[10] = r->w10;
+ if (len >= 11)
+ w[11] = (r->w11_hi << 3) | r->w11_lo;
+ if (len >= 11)
+ w[12] = r->w12;
+ if (len >= 12)
+ w[13] = r->w13;
+ if (len >= 13)
+ w[14] = r->w15;
+ if (len >= 13)
+ w[15] = (r->w14_hi << 2) | r->w14_lo;
+ if (len >= 14)
+ w[16] = (r->w16_hi << 3) | r->w16_lo;
+ if (len >= 14)
+ w[17] = r->w17;
+ if (len >= 15)
+ w[18] = r->w19;
+ if (len >= 15)
+ w[19] = (r->w18_hi << 3) | r->w18_lo;
+ if (len >= 16)
+ w[20] = (r->w20_hi << 3) | r->w20_lo;
+ if (len >= 16)
+ w[21] = r->w21;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + ((w[1] - 128 + w[2] - 1) % 255) + 1) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + ((w[1] + w[3] - 1) % 255) + 1) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + w[4] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + ((w[1] + ((w[3] - 64 + w[5] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + ((w[1] - 128 + ((w[2] + w[6] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + w[8] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] - 32 + w[9] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] - 32 + w[10] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 32 + w[11] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] + w[12] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] + w[13] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] + w[14] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + ((w[8] - 16 + w[16] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] - 32 + ((w[9] - 16 + w[17] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[18])
+ f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] - 32 + ((w[10] - 16 + w[18] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[19])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 32 + ((w[11] - 16 + w[19] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[20])
+ f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] + ((w[12] - 16 + w[20] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+ if (w[21])
+ f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] + ((w[13] - 16 + w[21] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..110. */
+ if ((cd[0] & 0xce & mask) == 0x8c) {
+ /* Range 128 format */
+ uint16_t w[29]; /* 1..28 */
+ struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd;
+
+ if (len < 3)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = r->w1;
+ if (len >= 4)
+ w[2] = r->w2;
+ if (len >= 5)
+ w[3] = (r->w3_hi << 4) | r->w3_lo;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 1) | r->w4_lo;
+ if (len >= 6)
+ w[5] = r->w5;
+ if (len >= 7)
+ w[6] = (r->w6_hi << 3) | r->w6_lo;
+ if (len >= 7)
+ w[7] = r->w7;
+ if (len >= 8)
+ w[8] = r->w8;
+ if (len >= 8)
+ w[9] = r->w9;
+ if (len >= 9)
+ w[10] = r->w10;
+ if (len >= 9)
+ w[11] = r->w11;
+ if (len >= 10)
+ w[12] = r->w12;
+ if (len >= 10)
+ w[13] = r->w13;
+ if (len >= 11)
+ w[14] = r->w14;
+ if (len >= 11)
+ w[15] = r->w15;
+ if (len >= 12)
+ w[16] = r->w16;
+ if (len >= 12)
+ w[17] = r->w17;
+ if (len >= 13)
+ w[18] = (r->w18_hi << 1) | r->w18_lo;
+ if (len >= 13)
+ w[19] = r->w19;
+ if (len >= 13)
+ w[20] = r->w20;
+ if (len >= 14)
+ w[21] = (r->w21_hi << 2) | r->w21_lo;
+ if (len >= 14)
+ w[22] = r->w22;
+ if (len >= 14)
+ w[23] = r->w23;
+ if (len >= 15)
+ w[24] = r->w24;
+ if (len >= 15)
+ w[25] = r->w25;
+ if (len >= 16)
+ w[26] = (r->w26_hi << 1) | r->w26_lo;
+ if (len >= 16)
+ w[27] = r->w27;
+ if (len >= 16)
+ w[28] = r->w28;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + ((w[1] - 64 + w[2] - 1) % 127) + 1) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + ((w[1] + w[3] - 1) % 127) + 1) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + w[4] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + ((w[1] + ((w[3] - 32 + w[5] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + ((w[1] - 64 + ((w[2] + w[6] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + w[8] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + w[9] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + w[10] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + w[11] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + w[12] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] + w[13] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] + w[14] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] - 8 + w[16] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + ((w[9] - 8 + w[17] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[18])
+ f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + ((w[10] - 8 + w[18] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[19])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + ((w[11] - 8 + w[19] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[20])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + ((w[12] - 8 + w[20] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[21])
+ f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] + ((w[13] - 8 + w[21] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[22])
+ f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] + ((w[14] - 8 + w[22] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[23])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] + ((w[15] - 8 + w[23] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[24])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] + w[24] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[25])
+ f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + ((w[9] + w[25] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[26])
+ f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + ((w[10] + w[26] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[27])
+ f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + ((w[11] + w[27] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+ if (w[28])
+ f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + ((w[12] + w[28] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..111. */
+ if ((cd[0] & 0xce & mask) == 0x8e) {
+ /* Variable bitmap format (can be any length >= 3) */
+ uint16_t orig = 0;
+ struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd;
+
+ if (len < 3)
+ return -EINVAL;
+ orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ f[orig].mask |= frqt;
+ for (i = 1; 2 + (i >> 3) < len; i++)
+ if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
+ f[(orig + i) % 1024].mask |= frqt;
+
+ return 0;
+ }
+
+ return 0;
+}
+
+/* decode "Cell Selection Parameters" (10.5.2.4) */
+static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s,
+ struct gsm48_cell_sel_par *cs)
+{
+#ifdef TODO
+ convert ms_txpwr_max_ccch dependant on the current frequenc and support
+ to the right powe level
+#endif
+ s->ms_txpwr_max_ccch = cs->ms_txpwr_max_ccch;
+ s->cell_resel_hyst_db = cs->cell_resel_hyst * 2;
+ s->rxlev_acc_min_db = cs->rxlev_acc_min - 110;
+ s->neci = cs->neci;
+ s->acs = cs->acs;
+
+ return 0;
+}
+
+/* decode "Cell Options (BCCH)" (10.5.2.3) */
+static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s,
+ struct gsm48_cell_options *co)
+{
+ s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
+ s->bcch_dtx = co->dtx;
+ s->bcch_pwrc = co->pwrc;
+
+ return 0;
+}
+
+/* decode "Cell Options (SACCH)" (10.5.2.3a) */
+static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s,
+ struct gsm48_cell_options *co)
+{
+ s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4;
+ s->sacch_dtx = co->dtx;
+ s->sacch_pwrc = co->pwrc;
+
+ return 0;
+}
+
+/* decode "Control Channel Description" (10.5.2.11) */
+static int gsm48_decode_ccd(struct gsm48_sysinfo *s,
+ struct gsm48_control_channel_descr *cc)
+{
+ s->ccch_conf = cc->ccch_conf;
+ s->bs_ag_blks_res = cc->bs_ag_blks_res;
+ s->att_allowed = cc->att;
+ s->pag_mf_periods = cc->bs_pa_mfrms + 2;
+ s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */
+
+ return 0;
+}
+
+/* decode "Mobile Allocation" (10.5.2.21) */
+static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s,
+ uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4)
+{
+ int i, j = 0;
+ uint16_t f[len << 3];
+
+ /* not more than 64 hopping indexes allowed in IE */
+ if (len > 8)
+ return -EINVAL;
+
+ /* tabula rasa */
+ *hopp_len = 0;
+ if (si4) {
+ for (i = 0; i < 1024; i++)
+ s->freq[i].mask &= ~FREQ_TYPE_HOPP;
+ }
+
+ /* generating list of all frequencies (1..1023,0) */
+ for (i = 1; i <= 1024; i++) {
+ if ((s->freq[i & 1023].mask & FREQ_TYPE_SERV)) {
+ LOGP(DRR, LOGL_INFO, "Serving cell ARFCN #%d: %d\n",
+ j, i & 1023);
+ f[j++] = i & 1023;
+ if (j == (len << 3))
+ break;
+ }
+ }
+
+ /* fill hopping table with frequency index given by IE
+ * and set hopping type bits
+ */
+ for (i = 0; i < (len << 3); i++) {
+ /* if bit is set, this frequency index is used for hopping */
+ if ((ma[len - 1 - (i >> 3)] & (1 << (i & 7)))) {
+ LOGP(DRR, LOGL_INFO, "Hopping ARFCN: %d (bit %d)\n",
+ i, f[i]);
+ /* index higher than entries in list ? */
+ if (i >= j) {
+ LOGP(DRR, LOGL_NOTICE, "Mobile Allocation "
+ "hopping index %d exceeds maximum "
+ "number of cell frequencies. (%d)\n",
+ i + 1, j);
+ break;
+ }
+ hopping[(*hopp_len)++] = f[i];
+ if (si4)
+ s->freq[f[i]].mask |= FREQ_TYPE_HOPP;
+ }
+ }
+
+ return 0;
+}
+
+/* Rach Control decode tables */
+static uint8_t gsm48_max_retrans[4] = {
+ 1, 2, 4, 7
+};
+static uint8_t gsm48_tx_integer[16] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50
+};
+
+/* decode "RACH Control Parameter" (10.5.2.29) */
+static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s,
+ struct gsm48_rach_control *rc)
+{
+ s->reest_denied = rc->re;
+ s->cell_barr = rc->cell_bar;
+ s->tx_integer = gsm48_tx_integer[rc->tx_integer];
+ s->max_retrans = gsm48_max_retrans[rc->max_trans];
+ s->class_barr = (rc->t2 << 8) | rc->t3;
+
+ return 0;
+}
+static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s,
+ struct gsm48_rach_control *rc)
+{
+ s->nb_reest_denied = rc->re;
+ s->nb_cell_barr = rc->cell_bar;
+ s->nb_tx_integer = gsm48_tx_integer[rc->tx_integer];
+ s->nb_max_retrans = gsm48_max_retrans[rc->max_trans];
+ s->nb_class_barr = (rc->t2 << 8) | rc->t3;
+
+ return 0;
+}
+
+/* decode "SI 1 Rest Octets" (10.5.2.32) */
+static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ return 0;
+}
+
+/* decode "SI 3 Rest Octets" (10.5.2.34) */
+static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data_len = len;
+ bv.data = si;
+
+ /* Optional Selection Parameters */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->sp = 1;
+ s->sp_cbq = bitvec_get_uint(&bv, 1);
+ s->sp_cro = bitvec_get_uint(&bv, 6);
+ s->sp_to = bitvec_get_uint(&bv, 3);
+ s->sp_pt = bitvec_get_uint(&bv, 5);
+ }
+ /* Optional Power Offset */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->po = 1;
+ s->po_value = bitvec_get_uint(&bv, 3);
+ }
+ /* System Onformation 2ter Indicator */
+ if (bitvec_get_bit_high(&bv) == H)
+ s->si2ter_ind = 1;
+ /* Early Classark Sending Control */
+ if (bitvec_get_bit_high(&bv) == H)
+ s->ecsm = 1;
+ /* Scheduling if and where */
+ if (bitvec_get_bit_high(&bv) == H) {
+ s->sched = 1;
+ s->sched_where = bitvec_get_uint(&bv, 3);
+ }
+ /* GPRS Indicator */
+ s->gi_ra_colour = bitvec_get_uint(&bv, 3);
+ s->gi_si13_pos = bitvec_get_uint(&bv, 1);
+ return 0;
+}
+
+/* decode "SI 4 Rest Octets" (10.5.2.35) */
+static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ return 0;
+}
+
+/* decode "SI 6 Rest Octets" (10.5.2.35a) */
+static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si,
+ uint8_t len)
+{
+ return 0;
+}
+
+/* send sysinfo event to other layers */
+static int gsm48_send_sysinfo(struct osmocom_ms *ms, uint8_t type)
+{
+ struct msgb *nmsg;
+ struct gsm322_msg *em;
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO);
+ if (!nmsg)
+ return -ENOMEM;
+ em = (struct gsm322_msg *) nmsg->data;
+ em->sysinfo = type;
+ gsm322_cs_sendmsg(ms, nmsg);
+
+ /* send timer info to location update process */
+ nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_SYSINFO);
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_mmevent_msg(ms, nmsg);
+
+ return 0;
+}
+
+/* receive "SYSTEM INFORMATION 1" message (9.1.31) */
+static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_1 *si = msgb_l3(msg);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 1 "
+ "ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 1 "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg))))
+ return 0;
+ memcpy(s->si1_msg, si, MIN(msgb_l3len(msg), sizeof(s->si1_msg)));
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n");
+
+ /* Cell Channel Description */
+ gsm48_decode_freq_list(&ms->support, s->freq,
+ si->cell_channel_description,
+ sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_param(s, &si->rach_control);
+ /* SI 1 Rest Octets */
+ if (payload_len)
+ gsm48_decode_si1_rest(s, si->rest_octets, payload_len);
+
+ s->si1 = 1;
+
+ return gsm48_send_sysinfo(ms, si->header.system_information);
+}
+
+/* receive "SYSTEM INFORMATION 2" message (9.1.32) */
+static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_2 *si = msgb_l3(msg);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2 "
+ "ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2 "
+ "message.\n");
+ return -EINVAL;
+ }
+//printf("len = %d\n", MIN(msgb_l3len(msg), sizeof(s->si2_msg)));
+
+ if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg))))
+ return 0;
+ memcpy(s->si2_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2_msg)));
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n");
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1;
+ gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2);
+ /* NCC Permitted */
+ s->nb_ncc_permitted = si->ncc_permitted;
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
+
+ s->si2 = 1;
+
+ return gsm48_send_sysinfo(ms, si->header.system_information);
+}
+
+/* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */
+static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_2bis *si = msgb_l3(msg);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2bis"
+ " ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2bis "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg),
+ sizeof(s->si2b_msg))))
+ return 0;
+ memcpy(s->si2b_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2b_msg)));
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n");
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ gsm48_decode_freq_list(&ms->support, s->freq,
+ si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0x8e,
+ FREQ_TYPE_NCELL_2bis);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_neigh(s, &si->rach_control);
+
+ s->si2bis = 1;
+
+ return gsm48_send_sysinfo(ms, si->header.system_information);
+}
+
+/* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */
+static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_2ter *si = msgb_l3(msg);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2ter"
+ " ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2ter "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg),
+ sizeof(s->si2t_msg))))
+ return 0;
+ memcpy(s->si2t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2t_msg)));
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n");
+
+ /* Neighbor Cell Description 2 */
+ s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3;
+ gsm48_decode_freq_list(&ms->support, s->freq,
+ si->ext_bcch_frequency_list,
+ sizeof(si->ext_bcch_frequency_list), 0x8e,
+ FREQ_TYPE_NCELL_2ter);
+
+ s->si2ter = 1;
+
+ return gsm48_send_sysinfo(ms, si->header.system_information);
+}
+
+/* receive "SYSTEM INFORMATION 3" message (9.1.35) */
+static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_3 *si = msgb_l3(msg);
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 3 "
+ "ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 3 "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg))))
+ return 0;
+ memcpy(s->si3_msg, si, MIN(msgb_l3len(msg), sizeof(s->si3_msg)));
+
+ /* Cell Identity */
+ s->cell_id = ntohs(si->cell_identity);
+ /* LAI */
+ gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ /* Control Channel Description */
+ gsm48_decode_ccd(s, &si->control_channel_desc);
+ /* Cell Options (BCCH) */
+ gsm48_decode_cellopt_bcch(s, &si->cell_options);
+ /* Cell Selection Parameters */
+ gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_param(s, &si->rach_control);
+ /* SI 3 Rest Octets */
+ if (payload_len >= 4)
+ gsm48_decode_si3_rest(s, si->rest_octets, payload_len);
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s "
+ "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac);
+
+ s->si3 = 1;
+
+ if (cs->ccch_mode == CCCH_MODE_NONE) {
+ cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED :
+ CCCH_MODE_NON_COMBINED;
+ LOGP(DRR, LOGL_NOTICE, "Changing CCCH_MODE to %d\n",
+ cs->ccch_mode);
+ l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode);
+ }
+
+ return gsm48_send_sysinfo(ms, si->header.system_information);
+}
+
+/* receive "SYSTEM INFORMATION 4" message (9.1.36) */
+static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_4 *si = msgb_l3(msg);
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si);
+ uint8_t *data = si->data;
+ struct gsm48_chan_desc *cd;
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 4 "
+ "ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ short_read:
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!s->si1) {
+ LOGP(DRR, LOGL_NOTICE, "Ignoring SYSTEM INFORMATION 4 "
+ "until SI 1 is received.\n");
+ return -EBUSY;
+ }
+
+ if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg))))
+ return 0;
+ memcpy(s->si4_msg, si, MIN(msgb_l3len(msg), sizeof(s->si4_msg)));
+
+ /* LAI */
+ gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ /* Cell Selection Parameters */
+ gsm48_decode_cell_sel_param(s, &si->cell_sel_par);
+ /* RACH Control Parameter */
+ gsm48_decode_rach_ctl_param(s, &si->rach_control);
+ /* CBCH Channel Description */
+ if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_CHAN_DESC) {
+ if (payload_len < 4)
+ goto short_read;
+ cd = (struct gsm48_chan_desc *) (data + 1);
+ if (cd->h0.h) {
+ s->h = 1;
+ gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn);
+ } else {
+ s->h = 0;
+ gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn);
+ }
+ payload_len -= 4;
+ data += 4;
+ }
+ /* CBCH Mobile Allocation */
+ if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_AL) {
+ if (payload_len < 1 || payload_len < 2 + data[1])
+ goto short_read;
+ gsm48_decode_mobile_alloc(s, data + 2, si->data[1], s->hopping,
+ &s->hopp_len, 1);
+ payload_len -= 2 + data[1];
+ data += 2 + data[1];
+ }
+ /* SI 4 Rest Octets */
+ if (payload_len > 0)
+ gsm48_decode_si4_rest(s, data, payload_len);
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s "
+ "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac);
+
+ s->si4 = 1;
+
+ return gsm48_send_sysinfo(ms, si->header.system_information);
+}
+
+/* receive "SYSTEM INFORMATION 5" message (9.1.37) */
+static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1;
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 "
+ "ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5 "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg))))
+ return 0;
+ memcpy(s->si5_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5_msg)));
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n");
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1;
+ gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5);
+
+ s->si5 = 1;
+
+ return gsm48_send_sysinfo(ms, si->system_information);
+}
+
+/* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */
+static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1;
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis"
+ " ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5bis "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n");
+
+ if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg),
+ sizeof(s->si5b_msg))))
+ return 0;
+ memcpy(s->si5b_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5b_msg)));
+
+ /* Neighbor Cell Description */
+ s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1;
+ s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1;
+ gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis);
+
+ s->si5bis = 1;
+
+ return gsm48_send_sysinfo(ms, si->system_information);
+}
+
+/* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */
+static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1;
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter"
+ " ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5ter "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n");
+
+ if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg),
+ sizeof(s->si5t_msg))))
+ return 0;
+ memcpy(s->si5t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5t_msg)));
+
+ /* Neighbor Cell Description */
+ gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list,
+ sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5ter);
+
+ s->si5ter = 1;
+
+ return gsm48_send_sysinfo(ms, si->system_information);
+}
+
+/* receive "SYSTEM INFORMATION 6" message (9.1.39) */
+static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg)
+{
+ /* NOTE: pseudo length is not in this structure, so we skip */
+ struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1;
+ struct gsm48_sysinfo *s = ms->cellsel.si;
+ int payload_len = msgb_l3len(msg) - sizeof(*si) - 1;
+
+ if (!s) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 "
+ "ignored\n");
+ return -EINVAL;
+ }
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 6 "
+ "message.\n");
+ return -EINVAL;
+ }
+
+ if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg))))
+ return 0;
+ memcpy(s->si6_msg, si, MIN(msgb_l3len(msg), sizeof(s->si6_msg)));
+
+ /* Cell Identity */
+ if (s->si6 && s->cell_id != ntohs(si->cell_identity))
+ LOGP(DRR, LOGL_INFO, "Cell ID on SI 6 differs from previous "
+ "read.\n");
+ s->cell_id = ntohs(si->cell_identity);
+ /* LAI */
+ gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac);
+ /* Cell Options (SACCH) */
+ gsm48_decode_cellopt_sacch(s, &si->cell_options);
+ /* NCC Permitted */
+ s->nb_ncc_permitted = si->ncc_permitted;
+ /* SI 6 Rest Octets */
+ if (payload_len >= 4)
+ gsm48_decode_si6_rest(s, si->rest_octets, payload_len);
+
+ LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s "
+ "lac 0x%04x)\n", gsm_print_mcc(s->mcc),
+ gsm_print_mnc(s->mnc), s->lac);
+
+ s->si6 = 1;
+
+ return gsm48_send_sysinfo(ms, si->system_information);
+}
+
+/*
+ * paging
+ */
+
+/* paging channel request */
+static int gsm48_rr_chan2cause[4] = {
+ RR_EST_CAUSE_ANS_PAG_ANY,
+ RR_EST_CAUSE_ANS_PAG_SDCCH,
+ RR_EST_CAUSE_ANS_PAG_TCH_F,
+ RR_EST_CAUSE_ANS_PAG_TCH_ANY
+};
+
+/* given LV of mobile identity is checked agains ms */
+static int gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi)
+{
+ char imsi[16];
+ uint32_t tmsi;
+ uint8_t mi_type;
+
+ if (mi[0] < 1)
+ return 0;
+ mi_type = mi[1] & GSM_MI_TYPE_MASK;
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ if (mi[0] < 5)
+ return 0;
+ memcpy(&tmsi, mi+2, 4);
+ if (ms->subscr.tmsi == ntohl(tmsi)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n",
+ ntohl(tmsi));
+
+ return 1;
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(tmsi));
+ break;
+ case GSM_MI_TYPE_IMSI:
+ gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]);
+ if (!strcmp(imsi, ms->subscr.imsi)) {
+ LOGP(DPAG, LOGL_INFO, "IMSI %s matches\n", imsi);
+
+ return 1;
+ } else
+ LOGP(DPAG, LOGL_INFO, "IMSI %s (not for us)\n", imsi);
+ break;
+ default:
+ LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n",
+ mi_type);
+ }
+
+ return 0;
+}
+
+/* 9.1.22 PAGING REQUEST 1 message received */
+static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_paging1 *pa = msgb_l3(msg);
+ int payload_len = msgb_l3len(msg) - sizeof(*pa);
+ int chan_1, chan_2;
+ uint8_t *mi;
+
+ /* empty paging request */
+ if (payload_len >= 2 && (pa->data[1] & GSM_MI_TYPE_MASK) == 0)
+ return 0;
+
+ /* 3.3.1.1.2: ignore paging while not camping on a cell */
+ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
+ || (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && cs->state != GSM322_C7_CAMPED_ANY_CELL)) {
+ LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");
+
+ return 0;
+ }
+ LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 1\n");
+
+ if (payload_len < 2) {
+ short_read:
+ LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 1 "
+ "message.\n");
+
+ return -EINVAL;
+ }
+
+ /* channel needed */
+ chan_1 = pa->cneed1;
+ chan_2 = pa->cneed2;
+ /* first MI */
+ mi = pa->data;
+ if (payload_len < mi[0] + 1)
+ goto short_read;
+ if (gsm_match_mi(ms, mi) > 0)
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
+ /* second MI */
+ payload_len -= mi[0] + 1;
+ mi = pa->data + mi[0] + 1;
+ if (payload_len < 2)
+ return 0;
+ if (mi[0] != GSM48_IE_MOBILE_ID)
+ return 0;
+ if (payload_len < mi[1] + 2)
+ goto short_read;
+ if (gsm_match_mi(ms, mi + 1) > 0)
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
+
+ return 0;
+}
+
+/* 9.1.23 PAGING REQUEST 2 message received */
+static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_paging2 *pa = msgb_l3(msg);
+ int payload_len = msgb_l3len(msg) - sizeof(*pa);
+ uint8_t *mi;
+ int chan_1, chan_2, chan_3;
+
+ /* 3.3.1.1.2: ignore paging while not camping on a cell */
+ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
+ || (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && cs->state != GSM322_C7_CAMPED_ANY_CELL)) {
+ LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");
+
+ return 0;
+ }
+ LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 2\n");
+
+ if (payload_len < 0) {
+ short_read:
+ LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 2 "
+ "message .\n");
+
+ return -EINVAL;
+ }
+
+ /* channel needed */
+ chan_1 = pa->cneed1;
+ chan_2 = pa->cneed2;
+ /* first MI */
+ if (ms->subscr.tmsi == ntohl(pa->tmsi1)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi1));
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(pa->tmsi1));
+ /* second MI */
+ if (ms->subscr.tmsi == ntohl(pa->tmsi2)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi2));
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(pa->tmsi2));
+ /* third MI */
+ mi = pa->data;
+ if (payload_len < 2)
+ return 0;
+ if (mi[0] != GSM48_IE_MOBILE_ID)
+ return 0;
+ if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */
+ goto short_read;
+ chan_3 = mi[mi[1] + 2] & 0x03; /* channel needed */
+ if (gsm_match_mi(ms, mi + 1) > 0)
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1);
+
+ return 0;
+}
+
+/* 9.1.24 PAGING REQUEST 3 message received */
+static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_paging3 *pa = msgb_l3(msg);
+ int payload_len = msgb_l3len(msg) - sizeof(*pa);
+ int chan_1, chan_2, chan_3, chan_4;
+
+ /* 3.3.1.1.2: ignore paging while not camping on a cell */
+ if (rr->state != GSM48_RR_ST_IDLE || !cs->selected
+ || (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && cs->state != GSM322_C7_CAMPED_ANY_CELL)) {
+ LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n");
+
+ return 0;
+ }
+ LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 3\n");
+
+ if (payload_len < 0) { /* must include "channel needed", part of *pa */
+ LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 3 "
+ "message .\n");
+
+ return -EINVAL;
+ }
+
+ /* channel needed */
+ chan_1 = pa->cneed1;
+ chan_2 = pa->cneed2;
+ chan_3 = pa->cneed3;
+ chan_4 = pa->cneed4;
+ /* first MI */
+ if (ms->subscr.tmsi == ntohl(pa->tmsi1)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi1));
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1);
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(pa->tmsi1));
+ /* second MI */
+ if (ms->subscr.tmsi == ntohl(pa->tmsi2)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi2));
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1);
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(pa->tmsi2));
+ /* thrid MI */
+ if (ms->subscr.tmsi == ntohl(pa->tmsi3)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi3));
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1);
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(pa->tmsi3));
+ /* fourth MI */
+ if (ms->subscr.tmsi == ntohl(pa->tmsi4)
+ && ms->subscr.tmsi_valid) {
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi4));
+ return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1);
+ } else
+ LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n",
+ ntohl(pa->tmsi4));
+
+ return 0;
+}
+
+/*
+ * (immediate) assignment
+ */
+
+/* match request reference agains request history */
+static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ int i;
+ struct gsm_time tm;
+ uint8_t ia_t1, ia_t2, ia_t3;
+
+ for (i = 0; i < 3; i++) {
+ /* filter confirmed RACH requests only */
+ if (rr->cr_hist[i].valid == 2
+ && ref->ra == rr->cr_hist[i].chan_req) {
+ ia_t1 = ref->t1;
+ ia_t2 = ref->t2;
+ ia_t3 = (ref->t3_high << 3) | ref->t3_low;
+ gsm_fn2gsmtime(&tm, rr->cr_hist[i].fn);
+ if (ia_t1 == (tm.t1 & 0x1f) && ia_t2 == tm.t2
+ && ia_t3 == tm.t3) {
+ LOGP(DRR, LOGL_INFO, "request %02x matches "
+ "(fn=%d,%d,%d)\n", ref->ra, ia_t1,
+ ia_t2, ia_t3);
+ return 1;
+ } else
+ LOGP(DRR, LOGL_INFO, "request %02x matches "
+ "but not frame number (IMM.ASS "
+ "fn=%d,%d,%d != RACH fn=%d,%d,%d)\n",
+ ref->ra, ia_t1, ia_t2, ia_t3,
+ tm.t1 & 0x1f, tm.t2, tm.t3);
+ }
+ }
+
+ return 0;
+}
+
+/* 9.1.18 IMMEDIATE ASSIGNMENT is received */
+static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_imm_ass *ia = msgb_l3(msg);
+ int ma_len = msgb_l3len(msg) - sizeof(*ia);
+ uint8_t ch_type, ch_subch, ch_ts;
+ struct gsm48_rr_cd cd;
+ uint8_t *st, st_len;
+
+ memset(&cd, 0, sizeof(cd));
+
+ if (ma_len < 0 /* mobile allocation IE must be included */
+ || ia->mob_alloc_len > ma_len) { /* short read of IE */
+ LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT "
+ "message.\n");
+ return -EINVAL;
+ }
+ if (ia->mob_alloc_len > 8) {
+ LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE "
+ "ASSIGNMENT too large.\n");
+ return -EINVAL;
+ }
+
+ /* starting time */
+ st_len = ma_len - ia->mob_alloc_len;
+ st = ia->mob_alloc + ia->mob_alloc_len;
+ if (st_len >= 3 && st[0] == GSM48_IE_START_TIME)
+ gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1));
+
+ /* decode channel description */
+ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n");
+ cd.chan_nr = ia->chan_desc.chan_nr;
+ rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (ia->chan_desc.h0.h) {
+ cd.h = 1;
+ gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio,
+ &cd.hsn);
+ LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x "
+ "MAIO %u HSN %u TS %u SS %u TSC %u)\n",
+ ia->timing_advance,
+ ia->timing_advance * GSM_TA_CM / 100,
+ ia->req_ref.ra, ia->chan_desc.chan_nr, cd.maio,
+ cd.hsn, ch_ts, ch_subch, cd.tsc);
+ } else {
+ cd.h = 0;
+ gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn);
+ LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x "
+ "ARFCN %u TS %u SS %u TSC %u)\n",
+ ia->timing_advance,
+ ia->timing_advance * GSM_TA_CM / 100,
+ ia->req_ref.ra, ia->chan_desc.chan_nr, cd.arfcn,
+ ch_ts, ch_subch, cd.tsc);
+ }
+
+ /* 3.3.1.1.2: ignore assignment while idle */
+ if (rr->state != GSM48_RR_ST_CONN_PEND || !rr->wait_assign) {
+ LOGP(DRR, LOGL_INFO, "Not for us, no request.\n");
+ return 0;
+ }
+
+ /* request ref */
+ if (gsm48_match_ra(ms, &ia->req_ref)) {
+ /* channel description */
+ memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now));
+ /* timing advance */
+ rr->cd_now.ta = ia->timing_advance;
+ /* mobile allocation */
+ memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
+ ia->mob_alloc_len + 1);
+ rr->wait_assign = 0;
+ return gsm48_rr_dl_est(ms);
+ }
+ LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");
+
+ return 0;
+}
+
+/* 9.1.19 IMMEDIATE ASSIGNMENT EXTENDED is received */
+static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_imm_ass_ext *ia = msgb_l3(msg);
+ int ma_len = msgb_l3len(msg) - sizeof(*ia);
+ uint8_t ch_type, ch_subch, ch_ts;
+ struct gsm48_rr_cd cd1, cd2;
+ uint8_t *st, st_len;
+
+ memset(&cd1, 0, sizeof(cd1));
+ memset(&cd2, 0, sizeof(cd2));
+
+ if (ma_len < 0 /* mobile allocation IE must be included */
+ || ia->mob_alloc_len > ma_len) { /* short read of IE */
+ LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT "
+ "EXTENDED message.\n");
+ return -EINVAL;
+ }
+ if (ia->mob_alloc_len > 4) {
+ LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE "
+ "ASSIGNMENT EXTENDED too large.\n");
+ return -EINVAL;
+ }
+
+ /* starting time */
+ st_len = ma_len - ia->mob_alloc_len;
+ st = ia->mob_alloc + ia->mob_alloc_len;
+ if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) {
+ gsm48_decode_start_time(&cd1,
+ (struct gsm48_start_time *)(st+1));
+ memcpy(&cd2, &cd1, sizeof(cd2));
+ }
+
+ /* decode channel description */
+ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n");
+ cd2.chan_nr = ia->chan_desc1.chan_nr;
+ rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (ia->chan_desc1.h0.h) {
+ cd1.h = 1;
+ gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio,
+ &cd1.hsn);
+ LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x "
+ "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n",
+ ia->timing_advance1,
+ ia->timing_advance1 * GSM_TA_CM / 100,
+ ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.maio,
+ cd1.hsn, ch_ts, ch_subch, cd1.tsc);
+ } else {
+ cd1.h = 0;
+ gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn);
+ LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x "
+ "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n",
+ ia->timing_advance1,
+ ia->timing_advance1 * GSM_TA_CM / 100,
+ ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.arfcn,
+ ch_ts, ch_subch, cd1.tsc);
+ }
+ cd2.chan_nr = ia->chan_desc2.chan_nr;
+ rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if (ia->chan_desc2.h0.h) {
+ cd2.h = 1;
+ gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio,
+ &cd2.hsn);
+ LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x "
+ "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n",
+ ia->timing_advance2,
+ ia->timing_advance2 * GSM_TA_CM / 100,
+ ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.maio,
+ cd2.hsn, ch_ts, ch_subch, cd2.tsc);
+ } else {
+ cd2.h = 0;
+ gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn);
+ LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x "
+ "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n",
+ ia->timing_advance2,
+ ia->timing_advance2 * GSM_TA_CM / 100,
+ ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.arfcn,
+ ch_ts, ch_subch, cd2.tsc);
+ }
+
+ /* 3.3.1.1.2: ignore assignment while idle */
+ if (rr->state != GSM48_RR_ST_CONN_PEND || !rr->wait_assign) {
+ LOGP(DRR, LOGL_INFO, "Not for us, no request.\n");
+ return 0;
+ }
+
+ /* request ref 1 */
+ if (gsm48_match_ra(ms, &ia->req_ref1)) {
+ /* channel description */
+ memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now));
+ /* timing advance */
+ rr->cd_now.ta = ia->timing_advance1;
+ /* mobile allocation */
+ memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
+ ia->mob_alloc_len + 1);
+ rr->wait_assign = 0;
+ return gsm48_rr_dl_est(ms);
+ }
+ /* request ref 1 */
+ if (gsm48_match_ra(ms, &ia->req_ref2)) {
+ /* channel description */
+ memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now));
+ /* timing advance */
+ rr->cd_now.ta = ia->timing_advance2;
+ /* mobile allocation */
+ memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len,
+ ia->mob_alloc_len + 1);
+ rr->wait_assign = 0;
+ return gsm48_rr_dl_est(ms);
+ }
+ LOGP(DRR, LOGL_INFO, "Request, but not for us.\n");
+
+ return 0;
+}
+
+/* 9.1.20 IMMEDIATE ASSIGNMENT REJECT is received */
+static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_imm_ass_rej *ia = msgb_l3(msg);
+ int payload_len = msgb_l3len(msg) - sizeof(*ia);
+ int i;
+ struct gsm48_req_ref *req_ref;
+ uint8_t t3122_value;
+
+ /* 3.3.1.1.2: ignore assignment while idle */
+ if (rr->state != GSM48_RR_ST_CONN_PEND || !rr->wait_assign)
+ return 0;
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT "
+ "REJECT message.\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ /* request reference */
+ req_ref = (struct gsm48_req_ref *)
+ (((uint8_t *)&ia->req_ref1) + i * 4);
+ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT "
+ "(ref 0x%02x)\n", req_ref->ra);
+ if (gsm48_match_ra(ms, req_ref)) {
+ /* wait indication */
+ t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4);
+ if (t3122_value)
+ start_rr_t3122(rr, t3122_value, 0);
+ /* start timer 3126 if not already */
+ if (!bsc_timer_pending(&rr->t3126))
+ start_rr_t3126(rr, 5, 0); /* TODO improve! */
+ /* stop assignmnet requests */
+ rr->n_chan_req = 0;
+
+ /* wait until timer 3126 expires, then release
+ * or wait for channel assignment */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* 9.1.1 ADDITIONAL ASSIGMENT is received */
+static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_add_ass *aa = (struct gsm48_add_ass *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*aa);
+ struct tlv_parsed tp;
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT "
+ "message.\n");
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0);
+
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+}
+
+/*
+ * measturement reports
+ */
+
+static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_rr_meas *meas = &rr->meas;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_meas_res *mr;
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_MEAS_REP;
+
+ /* measurement results */
+ mr->rxlev_full = meas->rxlev_full;
+ mr->rxlev_sub = meas->rxlev_sub;
+ mr->rxqual_full = meas->rxqual_full;
+ mr->rxqual_sub = meas->rxqual_sub;
+ mr->dtx_used = meas->dtx;
+ mr->ba_used = meas->ba;
+ mr->meas_valid = meas->meas_valid;
+ if (meas->ncell_na) {
+ /* no results for serving cells */
+ mr->no_nc_n_hi = 1;
+ mr->no_nc_n_lo = 3;
+ } else {
+ mr->no_nc_n_hi = meas->count >> 2;
+ mr->no_nc_n_lo = meas->count & 3;
+ }
+ mr->rxlev_nc1 = meas->rxlev_nc[0];
+ mr->rxlev_nc2_hi = meas->rxlev_nc[1] >> 1;
+ mr->rxlev_nc2_lo = meas->rxlev_nc[1] & 1;
+ mr->rxlev_nc3_hi = meas->rxlev_nc[2] >> 2;
+ mr->rxlev_nc3_lo = meas->rxlev_nc[2] & 3;
+ mr->rxlev_nc4_hi = meas->rxlev_nc[3] >> 3;
+ mr->rxlev_nc4_lo = meas->rxlev_nc[3] & 7;
+ mr->rxlev_nc5_hi = meas->rxlev_nc[4] >> 4;
+ mr->rxlev_nc5_lo = meas->rxlev_nc[4] & 15;
+ mr->rxlev_nc6_hi = meas->rxlev_nc[5] >> 5;
+ mr->rxlev_nc6_lo = meas->rxlev_nc[5] & 31;
+ mr->bsic_nc1_hi = meas->bsic_nc[0] >> 3;
+ mr->bsic_nc1_lo = meas->bsic_nc[0] & 7;
+ mr->bsic_nc2_hi = meas->bsic_nc[1] >> 4;
+ mr->bsic_nc2_lo = meas->bsic_nc[1] & 15;
+ mr->bsic_nc3_hi = meas->bsic_nc[2] >> 5;
+ mr->bsic_nc3_lo = meas->bsic_nc[2] & 31;
+ mr->bsic_nc4 = meas->bsic_nc[3];
+ mr->bsic_nc5 = meas->bsic_nc[4];
+ mr->bsic_nc6 = meas->bsic_nc[5];
+ mr->bcch_f_nc1 = meas->bcch_f_nc[0];
+ mr->bcch_f_nc2 = meas->bcch_f_nc[1];
+ mr->bcch_f_nc3 = meas->bcch_f_nc[2];
+ mr->bcch_f_nc4 = meas->bcch_f_nc[3];
+ mr->bcch_f_nc5_hi = meas->bcch_f_nc[4] >> 1;
+ mr->bcch_f_nc5_lo = meas->bcch_f_nc[4] & 1;
+ mr->bcch_f_nc6_hi = meas->bcch_f_nc[5] >> 2;
+ mr->bcch_f_nc6_lo = meas->bcch_f_nc[5] & 3;
+
+ return gsm48_send_rsl(ms, RSL_MT_UNIT_DATA_REQ, nmsg);
+}
+
+/*
+ * link establishment and release
+ */
+
+/* process "Loss Of Signal" */
+int gsm48_rr_los(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ uint8_t *mode;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n");
+
+ /* stop T3211 if running */
+ stop_rr_t3110(rr);
+
+ switch(rr->state) {
+ case GSM48_RR_ST_CONN_PEND:
+ LOGP(DRR, LOGL_INFO, "LOS during RACH request\n");
+
+ /* stop pending RACH timer */
+ stop_rr_t3126(rr);
+ break;
+ case GSM48_RR_ST_DEDICATED:
+ LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release "
+ "locally\n");
+
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* release message */
+ rel_local:
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = 1; /* local release */
+ /* start release */
+ return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+ case GSM48_RR_ST_REL_PEND:
+ LOGP(DRR, LOGL_INFO, "LOS during RR release procedure, release "
+ "locally\n");
+
+ /* stop pending RACH timer */
+ stop_rr_t3110(rr);
+
+ /* release locally */
+ goto rel_local;
+ default:
+ /* this should not happen */
+ LOGP(DRR, LOGL_ERROR, "LOS in IDLE state, ignoring\n");
+ return -EINVAL;
+ }
+
+ /* send inication to upper layer */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = RR_REL_CAUSE_LOST_SIGNAL;
+ gsm48_rr_upmsg(ms, nmsg);
+
+ /* return idle */
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+ return 0;
+}
+
+/* activate link and send establish request */
+static int gsm48_rr_dl_est(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_pag_rsp *pr;
+ uint8_t mi[11];
+ uint8_t ch_type, ch_subch, ch_ts;
+ uint16_t ma[64];
+ uint8_t ma_len;
+
+ /* 3.3.1.1.3.1 */
+ stop_rr_t3126(rr);
+
+ if (rr->cd_now.h) {
+ gsm48_decode_mobile_alloc(s, rr->cd_now.mob_alloc_lv + 1,
+ rr->cd_now.mob_alloc_lv[0], ma, &ma_len, 0);
+ if (ma_len < 1) {
+ LOGP(DRR, LOGL_NOTICE, "Hopping, but no allocation\n");
+ return -EINVAL;
+ }
+ }
+
+ /* send DL_EST_REQ */
+ if (rr->rr_est_msg) {
+ /* use queued message */
+ nmsg = rr->rr_est_msg;
+ rr->rr_est_msg = 0;
+ LOGP(DRR, LOGL_INFO, "sending establish message\n");
+ } else {
+ /* create paging response */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ pr = (struct gsm48_pag_rsp *) msgb_put(nmsg, sizeof(*pr));
+ /* key sequence */
+ pr->key_seq = subscr->key_seq;
+ /* classmark 2 */
+ pr->cm2_len = sizeof(pr->cm2);
+ gsm48_rr_enc_cm2(ms, &pr->cm2);
+ /* mobile identity */
+ if (ms->subscr.tmsi_valid) {
+ gsm48_generate_mid_from_tmsi(mi, subscr->tmsi);
+ LOGP(DRR, LOGL_INFO, "sending paging response with "
+ "TMSI\n");
+ } else if (subscr->imsi[0]) {
+ gsm48_generate_mid_from_imsi(mi, subscr->imsi);
+ LOGP(DRR, LOGL_INFO, "sending paging response with "
+ "IMSI\n");
+ } else {
+ mi[1] = 1;
+ mi[2] = 0xf0 | GSM_MI_TYPE_NONE;
+ LOGP(DRR, LOGL_INFO, "sending paging response without "
+ "TMSI/IMSI\n");
+ }
+ msgb_put(nmsg, 1 + mi[1]);
+ memcpy(pr->data, mi + 1, 1 + mi[1]);
+ }
+
+ /* activate channel */
+#ifdef TODO
+ RSL_MT_ to activate channel with all the cd_now informations
+#else
+ rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ if ((ch_type != RSL_CHAN_SDCCH8_ACCH
+ && ch_type != RSL_CHAN_SDCCH4_ACCH) || ch_ts > 4) {
+ printf("Channel type %d, subch %d, ts %d not supported, "
+ "exitting.\n", ch_type, ch_subch, ch_ts);
+ exit(-ENOTSUP);
+ }
+ if (rr->cd_now.h)
+ tx_ph_dm_est_req_h1(ms, rr->cd_now.maio, rr->cd_now.hsn,
+ ma, ma_len, rr->cd_now.chan_nr, rr->cd_now.tsc, 0);
+ else
+ tx_ph_dm_est_req_h0(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
+ rr->cd_now.tsc, 0);
+#endif
+
+ /* start establishmnet */
+ return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
+}
+
+/* the link is established */
+static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ uint8_t *mode;
+ struct msgb *nmsg;
+
+ /* if MM has releases before confirm, we start release */
+ if (rr->state == GSM48_RR_ST_REL_PEND) {
+ LOGP(DRR, LOGL_INFO, "MM already released RR.\n");
+ /* release message */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = 0; /* normal release */
+ /* start release */
+ return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+ }
+
+ /* 3.3.1.1.4 */
+ new_rr_state(rr, GSM48_RR_ST_DEDICATED);
+
+ /* send confirm to upper layer */
+ nmsg = gsm48_rr_msgb_alloc(
+ (rr->rr_est_req) ? GSM48_RR_EST_CNF : GSM48_RR_EST_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ return gsm48_rr_upmsg(ms, nmsg);
+}
+
+/* the link is released in pending state (by l2) */
+static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ LOGP(DSUM, LOGL_INFO, "Radio link is released\n");
+
+ /* send inication to upper layer */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = RR_REL_CAUSE_NORMAL;
+ gsm48_rr_upmsg(ms, nmsg);
+
+ /* start release timer, so UA will be transmitted */
+ start_rr_t_rel_wait(rr, 1, 500000);
+
+ /* pending release */
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ return 0;
+}
+
+/* 9.1.7 CHANNEL RELEASE is received */
+static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr);
+ struct tlv_parsed tp;
+ struct msgb *nmsg;
+ uint8_t *mode;
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE "
+ "message.\n");
+ return gsm48_rr_tx_rr_status(ms,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0);
+
+ LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x)\n",
+ cr->rr_cause);
+
+ /* BA range */
+ if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) {
+ gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE),
+ *(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range,
+ &rr->ba_ranges,
+ sizeof(rr->ba_range) / sizeof(rr->ba_range[0]));
+ /* NOTE: the ranges are kept until IDLE state is returned
+ * (see new_rr_state)
+ */
+ }
+
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* start T3110, so that two DISCs can be sent due to T200 timeout */
+ start_rr_t3110(rr, 1, 500000);
+
+ /* disconnect the main signalling link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = 0; /* normal release */
+ return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+}
+
+/*
+ * assignment and handover
+ */
+
+/* 9.1.3 sending ASSIGNMENT COMPLETE */
+static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_ass_cpl *ac;
+
+ LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+ /* RR_CAUSE */
+ ac->rr_cause = cause;
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* 9.1.4 sending ASSIGNMENT FAILURE */
+static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause)
+{
+ struct msgb *nmsg;
+ struct gsm48_hdr *gh;
+ struct gsm48_ass_fail *af;
+
+ LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause);
+
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh));
+ af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af));
+
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_ASS_COMPL;
+
+ /* RR_CAUSE */
+ af->rr_cause = cause;
+
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg);
+}
+
+/* 9.1.2 ASSIGNMENT COMMAND is received */
+static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+// struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac);
+ struct tlv_parsed tp;
+ struct gsm48_rr_cd cd;
+
+ LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n");
+
+ memset(&cd, 0, sizeof(cd));
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message.\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0);
+
+#if 0
+ /* channel description */
+ memcpy(&cd.chan_desc, &ac->chan_desc, sizeof(chan_desc));
+ /* power command */
+ cd.power_command = ac->power_command;
+ /* frequency list, after timer */
+ tlv_copy(&cd.fl, sizeof(fl_after), &tp, GSM48_IE_FRQLIST_AFTER);
+ /* cell channel description */
+ tlv_copy(&cd.ccd, sizeof(ccd), &tp, GSM48_IE_CELL_CH_DESC);
+ /* multislot allocation */
+ tlv_copy(&cd.multia, sizeof(ma), &tp, GSM48_IE_MSLOT_DESC);
+ /* channel mode */
+ tlv_copy(&cd.chanmode, sizeof(chanmode), &tp, GSM48_IE_CHANMODE_1);
+ /* mobile allocation, after time */
+ tlv_copy(&cd.moba_after, sizeof(moba_after), &tp, GSM48_IE_MOB_AL_AFTER);
+ /* starting time */
+ tlv_copy(&cd.start, sizeof(start), &tp, GSM_IE_START_TIME);
+ /* frequency list, before time */
+ tlv_copy(&cd.fl_before, sizeof(fl_before), &tp, GSM48_IE_FRQLIST_BEFORE);
+ /* channel description, before time */
+ tlv_copy(&cd.chan_desc_before, sizeof(cd_before), &tp, GSM48_IE_CHDES_1_BEFORE);
+ /* frequency channel sequence, before time */
+ tlv_copy(&cd.fcs_before, sizeof(fcs_before), &tp, GSM48_IE_FRQSEQ_BEFORE);
+ /* mobile allocation, before time */
+ tlv_copy(&cd.moba_before, sizeof(moba_before), &tp, GSM48_IE_MOB_AL_BEFORE);
+ /* cipher mode setting */
+ if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET))
+ cd.cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET);
+ else
+ cd.cipher = 0;
+
+ if (no CA) {
+ LOGP(DRR, LOGL_INFO, "No current cell allocation available.\n");
+ return gsm48_rr_tx_ass_fail(ms, GSM48_GSM48_RR_CAUSE_NO_CELL_ALLOC_A);
+ }
+
+ if (not supported) {
+ LOGP(DRR, LOGL_INFO, "New channel is not supported.\n");
+ return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT);
+ }
+
+ if (freq not supported) {
+ LOGP(DRR, LOGL_INFO, "New frequency is not supported.\n");
+ return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL);
+ }
+
+ /* store current channel descriptions, to return in case of failure */
+ memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
+ /* copy new description */
+ memcpy(&rr->chan_desc, cd, sizeof(cd));
+
+ /* start suspension of current link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg);
+
+ /* change into special assignment suspension state */
+ rr->assign_susp_state = 1;
+ rr->resume_last_state = 0;
+#else
+ return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL);
+#endif
+
+ return 0;
+}
+
+/*
+ * radio ressource requests
+ */
+
+/* establish request for dedicated mode */
+static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct gsm48_sysinfo *s = cs->si;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ uint8_t cause;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+ uint16_t acc_class;
+
+ /* 3.3.1.1.3.2 */
+ if (bsc_timer_pending(&rr->t3122)) {
+ if (rrh->cause != RR_EST_CAUSE_EMERGENCY) {
+ LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n");
+ cause = RR_REL_CAUSE_T3122;
+ reject:
+ LOGP(DSUM, LOGL_INFO, "Establishing radio link not "
+ "possible\n");
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = cause;
+ return gsm48_rr_upmsg(ms, nmsg);
+ }
+ LOGP(DRR, LOGL_INFO, "T3122 running, but emergency call\n");
+ stop_rr_t3122(rr);
+ }
+
+ /* if state is not idle */
+ if (rr->state != GSM48_RR_ST_IDLE) {
+ LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
+ /* cell selected */
+ if (!cs->selected) {
+ LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
+ /* check if camping */
+ if (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && rrh->cause != RR_EST_CAUSE_EMERGENCY) {
+ LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting!\n");
+ cause = RR_REL_CAUSE_EMERGENCY_ONLY;
+ goto reject;
+ }
+ if (cs->state != GSM322_C3_CAMPED_NORMALLY
+ && cs->state != GSM322_C7_CAMPED_ANY_CELL) {
+ LOGP(DRR, LOGL_INFO, "Not camping, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
+ /* check for relevant informations */
+ if (!s->si3) {
+ LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n");
+ cause = RR_REL_CAUSE_TRY_LATER;
+ goto reject;
+ }
+
+ /* 3.3.1.1.1 */
+ if (!subscr->acc_barr && s->cell_barr) {
+ LOGP(DRR, LOGL_INFO, "Cell barred, rejecting!\n");
+ cause = RR_REL_CAUSE_NOT_AUTHORIZED;
+ goto reject;
+ }
+ if (rrh->cause == RR_EST_CAUSE_EMERGENCY)
+ acc_class = subscr->acc_class | 0x0400;
+ else
+ acc_class = subscr->acc_class & 0xfbff;
+ if (!subscr->acc_barr && !(acc_class & (s->class_barr ^ 0xffff))) {
+ LOGP(DRR, LOGL_INFO, "Cell barred for our access class (access "
+ "%04x barred %04x)!\n", acc_class, s->class_barr);
+ cause = RR_REL_CAUSE_NOT_AUTHORIZED;
+ goto reject;
+ }
+
+ /* requested by RR */
+ rr->rr_est_req = 1;
+
+ /* clone and store REQUEST message */
+ if (!gh) {
+ LOGP(DRR, LOGL_ERROR, "Error, missing l3 message\n");
+ return -EINVAL;
+ }
+ rr->rr_est_msg = gsm48_l3_msgb_alloc();
+ if (!rr->rr_est_msg)
+ return -ENOMEM;
+ memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)),
+ msgb_l3(msg), msgb_l3len(msg));
+
+ /* request channel */
+ return gsm48_rr_chan_req(ms, rrh->cause, 0);
+}
+
+/* send all queued messages down to layer 2 */
+static int gsm48_rr_dequeue_down(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *msg;
+
+ while((msg = msgb_dequeue(&rr->downqueue))) {
+ LOGP(DRR, LOGL_INFO, "Sending queued message.\n");
+ gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg);
+ }
+
+ return 0;
+}
+
+/* 3.4.2 transfer data in dedicated mode */
+static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ if (rr->state != GSM48_RR_ST_DEDICATED) {
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ /* pull RR header */
+ msgb_pull(msg, sizeof(struct gsm48_rr_hdr));
+
+ /* queue message, during handover or assignment procedure */
+ if (rr->hando_susp_state || rr->assign_susp_state) {
+ LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n");
+ msgb_enqueue(&rr->downqueue, msg);
+ return 0;
+ }
+
+ /* forward message */
+ return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg);
+}
+
+/*
+ * data indications from data link
+ */
+
+/* 3.4.2 data from layer 2 to RR and upper layer*/
+static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_rr_hdr *rrh;
+ uint8_t pdisc = gh->proto_discr & 0x0f;
+
+ 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)
+ return 0;
+
+ switch(gh->msg_type) {
+ case GSM48_MT_RR_ADD_ASS:
+ rc = gsm48_rr_rx_add_ass(ms, msg);
+ break;
+ case GSM48_MT_RR_ASS_CMD:
+ rc = gsm48_rr_rx_ass_cmd(ms, msg);
+ break;
+#if 0
+ case GSM48_MT_RR_CIP_MODE_CMD:
+ rc = gsm48_rr_rx_cip_mode_cmd(ms, msg);
+ break;
+#endif
+ case GSM48_MT_RR_CLSM_ENQ:
+ rc = gsm48_rr_rx_cm_enq(ms, msg);
+ break;
+#if 0
+ case GSM48_MT_RR_HANDO_CMD:
+ rc = gsm48_rr_rx_hando_cmd(ms, msg);
+ break;
+ case GSM48_MT_RR_FREQ_REDEF:
+ rc = gsm48_rr_rx_freq_redef(ms, msg);
+ break;
+#endif
+ case GSM48_MT_RR_CHAN_REL:
+ rc = gsm48_rr_rx_chan_rel(ms, msg);
+ break;
+ default:
+ LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n",
+ gh->msg_type);
+
+ /* status message */
+ gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N);
+ }
+
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* pull off RSL header up to L3 message */
+ msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data);
+
+ /* push RR header */
+ msgb_push(msg, sizeof(struct gsm48_rr_hdr));
+ rrh = (struct gsm48_rr_hdr *)msg->data;
+ rrh->msg_type = GSM48_RR_DATA_IND;
+
+ return gsm48_rr_upmsg(ms, msg);
+}
+
+/* receive BCCH at RR layer */
+static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_SYSINFO_1:
+ return gsm48_rr_rx_sysinfo1(ms, msg);
+ case GSM48_MT_RR_SYSINFO_2:
+ return gsm48_rr_rx_sysinfo2(ms, msg);
+ case GSM48_MT_RR_SYSINFO_2bis:
+ return gsm48_rr_rx_sysinfo2bis(ms, msg);
+ case GSM48_MT_RR_SYSINFO_2ter:
+ return gsm48_rr_rx_sysinfo2ter(ms, msg);
+ case GSM48_MT_RR_SYSINFO_3:
+ return gsm48_rr_rx_sysinfo3(ms, msg);
+ case GSM48_MT_RR_SYSINFO_4:
+ return gsm48_rr_rx_sysinfo4(ms, msg);
+ default:
+#if 0
+ LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n",
+ sih->system_information);
+#endif
+ return -EINVAL;
+ }
+}
+
+/* receive CCCH at RR layer */
+static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_PAG_REQ_1:
+ return gsm48_rr_rx_pag_req_1(ms, msg);
+ case GSM48_MT_RR_PAG_REQ_2:
+ return gsm48_rr_rx_pag_req_2(ms, msg);
+ case GSM48_MT_RR_PAG_REQ_3:
+ return gsm48_rr_rx_pag_req_3(ms, msg);
+
+ case GSM48_MT_RR_IMM_ASS:
+ return gsm48_rr_rx_imm_ass(ms, msg);
+ case GSM48_MT_RR_IMM_ASS_EXT:
+ return gsm48_rr_rx_imm_ass_ext(ms, msg);
+ case GSM48_MT_RR_IMM_ASS_REJ:
+ return gsm48_rr_rx_imm_ass_rej(ms, msg);
+ default:
+ LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n",
+ sih->system_information);
+ return -EINVAL;
+ }
+}
+
+/* receive ACCH at RR layer */
+static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_SYSINFO_5:
+ return gsm48_rr_rx_sysinfo5(ms, msg);
+ case GSM48_MT_RR_SYSINFO_5bis:
+ return gsm48_rr_rx_sysinfo5bis(ms, msg);
+ case GSM48_MT_RR_SYSINFO_5ter:
+ return gsm48_rr_rx_sysinfo5ter(ms, msg);
+ case GSM48_MT_RR_SYSINFO_6:
+ return gsm48_rr_rx_sysinfo6(ms, msg);
+ default:
+ LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n",
+ sih->system_information);
+ return -EINVAL;
+ }
+}
+
+/* unit data from layer 2 to RR layer */
+static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm322_cellsel *cs = &ms->cellsel;
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct tlv_parsed tv;
+ uint8_t ch_type, ch_subch, ch_ts;
+
+ DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
+ rllh->chan_nr, rllh->link_id);
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
+ return -EIO;
+ }
+ msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
+
+ if (cs->ccch_state != GSM322_CCCH_ST_SYNC
+ && cs->ccch_state != GSM322_CCCH_ST_DATA)
+ return -EINVAL;
+
+ /* when camping, start/reset loss timer */
+ if (cs->state == GSM322_C3_CAMPED_NORMALLY
+ || cs->state == GSM322_C7_CAMPED_ANY_CELL) {
+ struct gsm48_sysinfo *s = &ms->cellsel.sel_si;
+#ifdef TODO
+ set radio link timeout on layer 1
+ it is the number of subsequent BCCH blocks. (about 1/4 seconds)
+#else
+ start_loss_timer(cs, s->bcch_radio_link_timeout / 4, 0);
+#endif
+ }
+
+ /* temporary moved here until confirm is fixed */
+ if (cs->ccch_state != GSM322_CCCH_ST_DATA) {
+ LOGP(DCS, LOGL_INFO, "Channel provides data.\n");
+ cs->ccch_state = GSM322_CCCH_ST_DATA;
+
+ /* in dedicated mode */
+ if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND)
+ return gsm48_rr_tx_rand_acc(ms, NULL);
+
+ /* set timer for reading BCCH */
+ if (cs->state == GSM322_C2_STORED_CELL_SEL
+ || cs->state == GSM322_C1_NORMAL_CELL_SEL
+ || cs->state == GSM322_C6_ANY_CELL_SEL
+ || cs->state == GSM322_C4_NORMAL_CELL_RESEL
+ || cs->state == GSM322_C8_ANY_CELL_RESEL
+ || cs->state == GSM322_C5_CHOOSE_CELL
+ || cs->state == GSM322_C9_CHOOSE_ANY_CELL
+ || cs->state == GSM322_PLMN_SEARCH
+ || cs->state == GSM322_HPLMN_SEARCH)
+ start_cs_timer(cs, ms->support.scan_to, 0);
+ // TODO: timer depends on BCCH config
+ }
+
+ rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts);
+ switch (ch_type) {
+ case RSL_CHAN_PCH_AGCH:
+ return gsm48_rr_rx_pch_agch(ms, msg);
+ case RSL_CHAN_BCCH:
+ return gsm48_rr_rx_bcch(ms, msg);
+ case RSL_CHAN_SDCCH4_ACCH:
+ return gsm48_rr_rx_acch(ms, msg);
+ case RSL_CHAN_SDCCH8_ACCH:
+ return gsm48_rr_rx_acch(ms, msg);
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n",
+ rllh->chan_nr);
+ return -EINVAL;
+ }
+}
+
+/* 3.4.13.3 RR abort in dedicated mode (also in conn. pending mode) */
+static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ uint8_t *mode;
+
+ /* stop pending RACH timer */
+ stop_rr_t3126(rr);
+
+ /* release "normally" if we are in dedicated mode */
+ if (rr->state == GSM48_RR_ST_DEDICATED) {
+ struct msgb *nmsg;
+
+ LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release "
+ "to layer 2.\n");
+
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ /* release message */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ mode = msgb_put(nmsg, 2);
+ mode[0] = RSL_IE_RELEASE_MODE;
+ mode[1] = 0; /* normal release */
+ return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg);
+ }
+
+ LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to "
+ "idle state.\n");
+ /* return idle */
+ new_rr_state(rr, GSM48_RR_ST_REL_PEND);
+
+ return 0;
+}
+
+/* release confirm in dedicated mode */
+static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ if (rr->hando_susp_state || rr->assign_susp_state) {
+ struct msgb *nmsg;
+
+ /* change radio to new channel */
+//todo tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
+// rr->cd_now.tsc);
+
+ /* send DL-ESTABLISH REQUEST */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg);
+
+#ifdef TODO
+ /* trigger RACH */
+ if (rr->hando_susp_state) {
+ gsm48_rr_tx_hando_access(ms);
+ rr->hando_acc_left = 3;
+ }
+#endif
+ }
+ return 0;
+}
+
+/* release confirm */
+static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm48_rr_hdr *nrrh;
+
+ LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n");
+
+ /* stop T3211 if running */
+ stop_rr_t3110(rr);
+
+ /* send release indication */
+ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND);
+ if (!nmsg)
+ return -ENOMEM;
+ nrrh = (struct gsm48_rr_hdr *)nmsg->data;
+ nrrh->cause = RR_REL_CAUSE_NORMAL;
+ gsm48_rr_upmsg(ms, nmsg);
+
+ /* return idle */
+ new_rr_state(rr, GSM48_RR_ST_IDLE);
+ return 0;
+}
+
+/*
+ * state machines
+ */
+
+/* state trasitions for link layer messages (lower layer) */
+static struct dldatastate {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} dldatastatelist[] = {
+ /* data transfer */
+ {SBIT(GSM48_RR_ST_IDLE) |
+ SBIT(GSM48_RR_ST_CONN_PEND) |
+ SBIT(GSM48_RR_ST_DEDICATED) |
+ SBIT(GSM48_RR_ST_REL_PEND),
+ RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind},
+
+ {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
+ RSL_MT_DATA_IND, gsm48_rr_data_ind},
+
+ /* esablish */
+ {SBIT(GSM48_RR_ST_CONN_PEND), /* 3.3.1.1.2 */
+ RSL_MT_CHAN_CNF, gsm48_rr_tx_rand_acc},
+
+ {SBIT(GSM48_RR_ST_IDLE) |
+ SBIT(GSM48_RR_ST_CONN_PEND) |
+ SBIT(GSM48_RR_ST_REL_PEND),
+ RSL_MT_EST_CONF, gsm48_rr_estab_cnf},
+
+#if 0
+ {SBIT(GSM48_RR_ST_DEDICATED),
+ RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated},
+
+ {SBIT(GSM_RRSTATE),
+ RSL_MT_CONNECT_CNF, gsm48_rr_connect_cnf},
+
+#endif
+
+ /* release */
+ {SBIT(GSM48_RR_ST_CONN_PEND) |
+ SBIT(GSM48_RR_ST_DEDICATED),
+ RSL_MT_REL_IND, gsm48_rr_rel_ind},
+
+ {SBIT(GSM48_RR_ST_REL_PEND),
+ RSL_MT_REL_CONF, gsm48_rr_rel_cnf},
+
+ /* suspenion */
+ {SBIT(GSM48_RR_ST_DEDICATED),
+ RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated},
+
+#if 0
+ {SBIT(GSM48_RR_ST_DEDICATED),
+ RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated},
+
+ {SBIT(GSM_RRSTATE),
+ RSL_MT_MDL_ERROR_IND, gsm48_rr_mdl_error_ind},
+#endif
+};
+
+#define DLDATASLLEN \
+ (sizeof(dldatastatelist) / sizeof(struct dldatastate))
+
+static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int msg_type = rllh->c.msg_type;
+ int i;
+ int rc;
+
+ if (msg_type != RSL_MT_UNIT_DATA_IND) {
+ LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state "
+ "%s\n", ms->name, get_rsl_name(msg_type),
+ gsm48_rr_state_names[rr->state]);
+ }
+
+ /* find function for current state and message */
+ for (i = 0; i < DLDATASLLEN; i++)
+ if ((msg_type == dldatastatelist[i].type)
+ && ((1 << rr->state) & dldatastatelist[i].states))
+ break;
+ if (i == DLDATASLLEN) {
+ LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = dldatastatelist[i].rout(ms, msg);
+
+ /* free msgb unless it is forwarded */
+ if (dldatastatelist[i].rout != gsm48_rr_data_ind)
+ msgb_free(msg);
+
+ return rc;
+}
+
+/* state trasitions for RR-SAP messages from up */
+static struct rrdownstate {
+ uint32_t states;
+ int type;
+ int (*rout) (struct osmocom_ms *ms, struct msgb *msg);
+} rrdownstatelist[] = {
+ /* NOTE: If not IDLE, it is rejected there. */
+ {ALL_STATES, /* 3.3.1.1 */
+ GSM48_RR_EST_REQ, gsm48_rr_est_req},
+
+ {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */
+ GSM48_RR_DATA_REQ, gsm48_rr_data_req},
+
+ {SBIT(GSM48_RR_ST_CONN_PEND) |
+ SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */
+ GSM48_RR_ABORT_REQ, gsm48_rr_abort_req},
+
+#if 0
+ {SBIT(GSM48_RR_ST_DEDICATED),
+ GSM48_RR_ACT_REQ, gsm48_rr_act_req},
+#endif
+};
+
+#define RRDOWNSLLEN \
+ (sizeof(rrdownstatelist) / sizeof(struct rrdownstate))
+
+int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data;
+ int msg_type = rrh->msg_type;
+ int i;
+ int rc;
+
+ LOGP(DRR, LOGL_INFO, "(ms %s) Message '%s' received in state %s\n",
+ ms->name, get_rr_name(msg_type),
+ gsm48_rr_state_names[rr->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < RRDOWNSLLEN; i++)
+ if ((msg_type == rrdownstatelist[i].type)
+ && ((1 << rr->state) & rrdownstatelist[i].states))
+ break;
+ if (i == RRDOWNSLLEN) {
+ LOGP(DRR, LOGL_NOTICE, "Message unhandled at this state.\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = rrdownstatelist[i].rout(ms, msg);
+
+ /* free msgb uless it is forwarded */
+ if (rrdownstatelist[i].rout != gsm48_rr_data_req)
+ msgb_free(msg);
+
+ return rc;
+}
+
+/*
+ * init/exit
+ */
+
+int gsm48_rr_init(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+
+ memset(rr, 0, sizeof(*rr));
+ rr->ms = ms;
+
+ LOGP(DRR, LOGL_INFO, "init Radio Ressource process\n");
+
+ INIT_LLIST_HEAD(&rr->rsl_upqueue);
+ INIT_LLIST_HEAD(&rr->downqueue);
+ /* downqueue is handled here, so don't add_work */
+
+ osmol2_register_handler(ms, &gsm48_rx_rsl);
+
+ return 0;
+}
+
+int gsm48_rr_exit(struct osmocom_ms *ms)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *msg;
+
+ LOGP(DRR, LOGL_INFO, "exit Radio Ressource process\n");
+
+ /* flush queues */
+ while ((msg = msgb_dequeue(&rr->rsl_upqueue)))
+ msgb_free(msg);
+ while ((msg = msgb_dequeue(&rr->downqueue)))
+ msgb_free(msg);
+
+ if (rr->rr_est_msg) {
+ msgb_free(rr->rr_est_msg);
+ rr->rr_est_msg = NULL;
+ }
+
+ stop_rr_t_rel_wait(rr);
+ stop_rr_t3110(rr);
+ stop_rr_t3122(rr);
+ stop_rr_t3126(rr);
+
+ return 0;
+}
+
+#if 0
+
+the process above is complete
+------------------------------------------------------------------------------
+incomplete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+todo:
+
+stop timers on abort
+wird beim abbruch immer der gepufferte cm-service-request entfernt, auch beim verschicken?:
+measurement reports
+todo rr_sync_ind when receiving ciph, re ass, channel mode modify
+
+todo change procedures, release procedure
+
+static int gsm48_rr_act_req(struct osmocom_ms *ms, struct gsm48_rr *rrmsg)
+{
+}
+
+
+}
+
+/* memcopy of LV of given IE from tlv_parsed structure */
+static int tlv_copy(void *dest, int dest_len, struct tlv_parsed *tp, uint8_t ie)
+{
+ uint8_t *lv = dest;
+ uint8_t len;
+
+ if (dest_len < 1)
+ return -EINVAL;
+ lv[0] = 0;
+
+ if (!TLVP_PRESENT(tp, ie))
+ return 0;
+
+ len = TLVP_LEN(tp, ie);
+ if (len < 1)
+ return 0;
+ if (len + 1 > dest_len)
+ return -ENOMEM;
+
+ memcpy(dest, TLVP_VAL(tp, ie) - 1, len + 1);
+ return 0;
+}
+
+
+/* decode "Cell Description" (10.5.2.2) */
+static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc)
+{
+ *arfcn = (cd->bcch_hi << 8) + cd->bcch_lo;
+ *ncc = cd->ncc;
+ *bcc = cd->bcc;
+}
+
+/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */
+static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, uint8_t *power_level uint8_t *atc)
+{
+ *power_level = pc->power_level;
+ if (atc) /* only in case of 10.5.2.28a */
+ *atc = pc->atc;
+}
+
+/* decode "Synchronization Indication" (10.5.2.39) */
+static int gsm48_decode_power_cmd_acc(struct gsm48_rrlayer *rr, struct gsm48_rr_sync_ind *si)
+{
+ rr->ho_sync_ind = si->si;
+ rr->ho_rot = si->rot;
+ rr->ho_nci = si->nci;
+}
+
+/* receiving HANDOVER COMMAND message (9.1.15) */
+static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = ms->rrlayer;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data;
+ int payload_len = msgb_l3len(msg) - sizeof(*gh) - wirklich sizeof(*ho);
+ struct tlv_parsed tp;
+ struct gsm48_rr_cd cd;
+ struct msgb *nmsg;
+
+ memset(&cd, 0, sizeof(cd));
+
+ if (payload_len < 0) {
+ LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message.\n");
+ return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC);
+ }
+ tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0);
+
+ /* decode Cell Description */
+ gsm_decode_cell_desc(&ho->cell_desc, &cd.bcch_arfcn, &cd.ncc, &cd.bcc);
+ /* Channel Description */
+ memcpy(&rr->chan_desc.chan_desc, ho->chan_desc, 3);
+ /* Handover Reference */
+ rr->hando_ref = ho->ho_ref;
+ /* Power Command and access type */
+ gsm_decode_power_cmd_acc((struct gsm48_power_cmd *)&ho->power_command,
+ &cd.power_level, cd.atc);
+ /* Synchronization Indication */
+ if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND))
+ gsm48_decode_sync_ind(rr,
+ TLVP_VAL(&tp, GSM48_IE_SYNC_IND)-1, &cd);
+ /* Frequency Sort List */
+ if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_SHORT_LIST))
+ gsm48_decode_freq_list(&ms->support, s->freq,
+ TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST),
+ *(TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST)-1),
+ 0xce, FREQ_TYPE_SERV);
+
+
+today: more IE parsing
+
+ /* store current channel descriptions, to return in case of failure */
+ memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd));
+ /* copy new description */
+ memcpy(&rr->chan_desc, cd, sizeof(cd));
+
+ /* start suspension of current link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg);
+
+ /* change into special handover suspension state */
+ rr->hando_susp_state = 1;
+ rr->resume_last_state = 0;
+
+ return 0;
+}
+
+static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+ if (rr->hando_susp_state || rr->assign_susp_state) {
+ if (rr->resume_last_state) {
+ rr->resume_last_state = 0;
+ gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL);
+ } else {
+ gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_PROTO_ERR_UNSPEC);
+ }
+ /* transmit queued frames during ho / ass transition */
+ gsm48_rr_dequeue_down(ms);
+ }
+
+ return 0;
+}
+
+static int gsm48_rr_connect_cnf(struct osmocom_ms *ms, struct msgbl *msg)
+{
+}
+
+static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = ms->rrlayer;
+ struct msgb *nmsg;
+ struct gsm_rr_hdr *nrrh;
+
+ printing of the cause
+
+ switch (msg->l3h[0]) {
+ case RLL_CAUSE_SEQ_ERR:
+ case RLL_CAUSE_UNSOL_DM_RESP_MF:
+ einige muessen ignoriert werden
+ andere gelten als release
+ }
+
+ if (rr->hando_susp_state || rr->assign_susp_state) {
+ if (!rr->resume_last_state) {
+ rr->resume_last_state = 1;
+
+ /* get old channel description */
+ memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
+
+ /* change radio to old channel */
+ tx_ph_dm_est_req(ms, rr->cd_now.arfcn,
+ rr->cd_now.chan_nr, rr->cd_now.tsc);
+
+ /* re-establish old link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg);
+ }
+ rr->resume_last_state = 0;
+ }
+
+ /* deactivate channel */
+ tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr);
+
+ /* send abort ind to upper layer */
+ nmsg = gsm48_mm_msgb_alloc();
+
+ if (!msg)
+ return -ENOMEM;
+ nrrh = (struct gsm_mm_hdr *)nmsg->data;
+ nrrh->msg_type = RR_ABORT_IND;
+ nrrh->cause = GSM_MM_CAUSE_LINK_FAILURE;
+ return gsm48_rr_upmsg(ms, msg);
+}
+
+static void timeout_rr_t3124(void *arg)
+{
+ struct gsm48_rrlayer *rr = arg;
+ struct msgb *nmsg;
+
+ /* stop sending more access bursts when timer expired */
+ hando_acc_left = 0;
+
+ /* get old channel description */
+ memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd));
+
+ /* change radio to old channel */
+ tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr,
+ rr->cd_now.tsc);
+
+ /* re-establish old link */
+ nmsg = gsm48_l3_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg);
+
+ todo
+}
+
+/* send HANDOVER ACCESS burst (9.1.14) */
+static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms)
+{
+ nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS");
+ if (!nmsg)
+ return -ENOMEM;
+ *msgb_put(nmsg, 1) = rr->hando_ref;
+ todo burst
+ return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg);
+}
+
+/* send next channel request in dedicated state */
+static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct gsm48_rrlayer *rr = &ms->rrlayer;
+ struct msgb *nmsg;
+ int s;
+
+ if (!rr->hando_susp_state) {
+ LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n");
+ return 0;
+ }
+
+ /* send up to four handover access bursts */
+ if (rr->hando_acc_left) {
+ rr->hando_acc_left--;
+ gsm48_rr_tx_hando_access(ms);
+ return;
+ }
+
+ /* start timer for sending next HANDOVER ACCESS bursts afterwards */
+ if (!bsc_timer_pending(&rr->t3124)) {
+ if (allocated channel is SDCCH)
+ start_rr_t3124(rr, GSM_T3124_675);
+ else
+ start_rr_t3124(rr, GSM_T3124_320);
+ if (!rr->n_chan_req) {
+ start_rr_t3126(rr, 5, 0); /* TODO improve! */
+ return 0;
+ }
+ rr->n_chan_req--;
+
+ /* wait for PHYSICAL INFORMATION message or T3124 timeout */
+ return 0;
+
+}
+
+#endif
+
+
diff --git a/src/host/layer23/src/gsmtap_util.c b/src/host/layer23/src/gsmtap_util.c
new file mode 100644
index 00000000..a63fc882
--- /dev/null
+++ b/src/host/layer23/src/gsmtap_util.c
@@ -0,0 +1,179 @@
+/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */
+/*
+ * (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 <osmocom/osmocom_data.h>
+#include <osmocom/gsmtap_util.h>
+#include <osmocore/logging.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/gsmtap.h>
+#include <osmocore/msgb.h>
+#include <osmocore/rsl.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+static struct bsc_fd gsmtap_bfd = { .fd = -1 };
+static LLIST_HEAD(gsmtap_txqueue);
+
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
+{
+ uint8_t ret = GSMTAP_CHANNEL_UNKNOWN;
+
+ switch (rsl_chantype) {
+ case RSL_CHAN_Bm_ACCHs:
+ ret = GSMTAP_CHANNEL_TCH_F;
+ break;
+ case RSL_CHAN_Lm_ACCHs:
+ ret = GSMTAP_CHANNEL_TCH_H;
+ break;
+ case RSL_CHAN_SDCCH4_ACCH:
+ ret = GSMTAP_CHANNEL_SDCCH4;
+ break;
+ case RSL_CHAN_SDCCH8_ACCH:
+ ret = GSMTAP_CHANNEL_SDCCH8;
+ break;
+ case RSL_CHAN_BCCH:
+ ret = GSMTAP_CHANNEL_BCCH;
+ break;
+ case RSL_CHAN_RACH:
+ ret = GSMTAP_CHANNEL_RACH;
+ break;
+ case RSL_CHAN_PCH_AGCH:
+ /* it could also be AGCH... */
+ ret = GSMTAP_CHANNEL_PCH;
+ break;
+ }
+
+ if (link_id & 0x40)
+ ret |= GSMTAP_CHANNEL_ACCH;
+
+ return ret;
+}
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
+ uint32_t fn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, unsigned int len)
+{
+ struct msgb *msg;
+ struct gsmtap_hdr *gh;
+ uint8_t *dst;
+
+ /* gsmtap was never initialized, so don't try to send anything */
+ if (gsmtap_bfd.fd == -1)
+ return 0;
+
+ msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx");
+ if (!msg)
+ return -ENOMEM;
+
+ gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
+
+ gh->version = GSMTAP_VERSION;
+ gh->hdr_len = sizeof(*gh)/4;
+ gh->type = GSMTAP_TYPE_UM;
+ gh->timeslot = ts;
+ gh->sub_slot = ss;
+ gh->arfcn = htons(arfcn);
+ gh->snr_db = snr;
+ gh->signal_dbm = signal_dbm;
+ gh->frame_number = htonl(fn);
+ gh->sub_type = chan_type;
+ gh->antenna_nr = 0;
+
+ dst = msgb_put(msg, len);
+ memcpy(dst, data, len);
+
+ msgb_enqueue(&gsmtap_txqueue, msg);
+ gsmtap_bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+/* Callback from select layer if we can write to the socket */
+static int gsmtap_fd_cb(struct bsc_fd *fd, unsigned int flags)
+{
+ struct msgb *msg;
+ int rc;
+
+ if (!(flags & BSC_FD_WRITE))
+ return 0;
+
+ msg = msgb_dequeue(&gsmtap_txqueue);
+ if (!msg) {
+ /* no more messages in the queue, disable READ cb */
+ gsmtap_bfd.when = 0;
+ return 0;
+ }
+ rc = write(gsmtap_bfd.fd, msg->data, msg->len);
+ if (rc < 0) {
+ perror("writing msgb to gsmtap fd");
+ msgb_free(msg);
+ return rc;
+ }
+ if (rc != msg->len) {
+ perror("short write to gsmtap fd");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ msgb_free(msg);
+ return 0;
+}
+
+int gsmtap_init(uint32_t dst_ip)
+{
+ int rc;
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(GSMTAP_UDP_PORT);
+ sin.sin_addr.s_addr = htonl(dst_ip);
+
+ /* FIXME: create socket */
+ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0) {
+ perror("creating UDP socket");
+ return rc;
+ }
+ gsmtap_bfd.fd = rc;
+ rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin));
+ if (rc < 0) {
+ perror("connecting UDP socket");
+ close(gsmtap_bfd.fd);
+ gsmtap_bfd.fd = 0;
+ return rc;
+ }
+
+ gsmtap_bfd.when = BSC_FD_WRITE;
+ gsmtap_bfd.cb = gsmtap_fd_cb;
+ gsmtap_bfd.data = NULL;
+
+ return bsc_register_fd(&gsmtap_bfd);
+}
diff --git a/src/host/layer23/src/l1ctl.c b/src/host/layer23/src/l1ctl.c
new file mode 100644
index 00000000..8d5e5693
--- /dev/null
+++ b/src/host/layer23/src/l1ctl.c
@@ -0,0 +1,577 @@
+/* Layer1 control code, talking L1CTL protocol with L1 on the phone */
+
+/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <l1a_l23_interface.h>
+
+#include <osmocore/signal.h>
+#include <osmocore/logging.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/protocol/gsm_08_58.h>
+#include <osmocore/rsl.h>
+
+#include <osmocom/l1ctl.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/l1l2_interface.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/logging.h>
+#include <osmocom/gsmtap_util.h>
+
+static struct msgb *osmo_l1_alloc(uint8_t msg_type)
+{
+ struct l1ctl_hdr *l1h;
+ struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1");
+
+ if (!msg) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n");
+ return NULL;
+ }
+
+ msg->l1h = msgb_put(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = msg_type;
+
+ return msg;
+}
+
+
+static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn)
+{
+ /* TODO: Include the band */
+ return arfcn;
+}
+
+static int rx_l1_fbsb_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_info_dl *dl;
+ struct l1ctl_fbsb_conf *sb;
+ struct gsm_time tm;
+
+ if (msgb_l3len(msg) < sizeof(*dl) + sizeof(*sb)) {
+ LOGP(DL1C, LOGL_ERROR, "FBSB RESP: MSG too short %u\n",
+ msgb_l3len(msg));
+ return -1;
+ }
+
+ dl = (struct l1ctl_info_dl *) msg->l1h;
+ sb = (struct l1ctl_fbsb_conf *) dl->payload;
+
+ printf("snr=%04x, arfcn=%u result=%u\n", dl->snr, ntohs(dl->band_arfcn),
+ sb->result);
+
+ if (sb->result != 0) {
+ LOGP(DL1C, LOGL_ERROR, "FBSB RESP: result=%u\n", sb->result);
+ dispatch_signal(SS_L1CTL, S_L1CTL_FBSB_ERR, ms);
+ return 0;
+ }
+
+ gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
+ DEBUGP(DL1C, "SCH: SNR: %u TDMA: (%.4u/%.2u/%.2u) bsic: %d\n",
+ dl->snr, tm.t1, tm.t2, tm.t3, sb->bsic);
+ dispatch_signal(SS_L1CTL, S_L1CTL_FBSB_RESP, ms);
+
+ return 0;
+}
+
+static int rx_l1_rach_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_info_dl *dl;
+ struct osmobb_rach_conf rc;
+
+ if (msgb_l3len(msg) < sizeof(*dl)) {
+ LOGP(DL1C, LOGL_ERROR, "RACH CONF: MSG too short %u\n",
+ msgb_l3len(msg));
+ return -1;
+ }
+
+ dl = (struct l1ctl_info_dl *) msg->l1h;
+
+ rc.fn = htonl(dl->frame_nr);
+ rc.ms = ms;
+ dispatch_signal(SS_L1CTL, S_L1CTL_RACH_CONF, &rc);
+
+ return 0;
+}
+
+char *chan_nr2string(uint8_t chan_nr)
+{
+ static char str[20];
+ uint8_t cbits = chan_nr >> 3;
+
+ str[0] = '\0';
+
+ if (cbits == 0x01)
+ sprintf(str, "TCH/F");
+ else if ((cbits & 0x1e) == 0x02)
+ sprintf(str, "TCH/H(%u)", cbits & 0x01);
+ else if ((cbits & 0x1c) == 0x04)
+ sprintf(str, "SDCCH/4(%u)", cbits & 0x03);
+ else if ((cbits & 0x18) == 0x08)
+ sprintf(str, "SDCCH/8(%u)", cbits & 0x07);
+ else if (cbits == 0x10)
+ sprintf(str, "BCCH");
+ else if (cbits == 0x11)
+ sprintf(str, "RACH");
+ else if (cbits == 0x12)
+ sprintf(str, "PCH/AGCH");
+ else
+ sprintf(str, "UNKNOWN");
+
+ return str;
+}
+
+/* Receive L1CTL_DATA_IND (Data Indication from L1) */
+static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_info_dl *dl, dl_cpy;
+ struct l1ctl_data_ind *ccch;
+ struct lapdm_entity *le;
+ uint8_t chan_type, chan_ts, chan_ss;
+ uint8_t gsmtap_chan_type;
+ struct gsm_time tm;
+
+ if (msgb_l3len(msg) < sizeof(*ccch)) {
+ LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n",
+ msgb_l3len(msg));
+ msgb_free(msg);
+ return -1;
+ }
+
+ dl = (struct l1ctl_info_dl *) msg->l1h;
+ msg->l2h = dl->payload;
+ ccch = (struct l1ctl_data_ind *) msg->l2h;
+
+ gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
+ rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
+ DEBUGP(DL1C, "%s (%.4u/%.2u/%.2u) %s\n",
+ chan_nr2string(dl->chan_nr), tm.t1, tm.t2, tm.t3,
+ hexdump(ccch->data, sizeof(ccch->data)));
+
+ if (dl->num_biterr) {
+ LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n",
+ dl->num_biterr);
+ return 0;
+ }
+
+ /* send CCCH data via GSMTAP */
+ gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id);
+ gsmtap_sendmsg(ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss,
+ tm.fn, dl->rx_level-110, dl->snr, ccch->data,
+ sizeof(ccch->data));
+
+ /* determine LAPDm entity based on SACCH or not */
+ if (dl->link_id & 0x40)
+ le = &ms->l2_entity.lapdm_acch;
+ else
+ le = &ms->l2_entity.lapdm_dcch;
+ /* make local stack copy of l1ctl_info_dl, as LAPDm will
+ * overwrite skb hdr */
+ memcpy(&dl_cpy, dl, sizeof(dl_cpy));
+
+ /* pull the L1 header from the msgb */
+ msgb_pull(msg, msg->l2h - (msg->l1h-sizeof(struct l1ctl_hdr)));
+ msg->l1h = NULL;
+
+ /* send it up into LAPDm */
+ l2_ph_data_ind(msg, le, &dl_cpy);
+
+ return 0;
+}
+
+/* Receive L1CTL_DATA_CONF (Data Confirm from L1) */
+static int rx_ph_data_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_info_dl *dl;
+ struct lapdm_entity *le;
+
+ dl = (struct l1ctl_info_dl *) msg->l1h;
+
+ /* determine LAPDm entity based on SACCH or not */
+ if (dl->link_id & 0x40)
+ le = &ms->l2_entity.lapdm_acch;
+ else
+ le = &ms->l2_entity.lapdm_dcch;
+
+ /* send it up into LAPDm */
+ l2_ph_data_conf(msg, le);
+
+ return 0;
+}
+
+/* Transmit L1CTL_DATA_REQ */
+int tx_ph_data_req(struct osmocom_ms *ms, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id)
+{
+ struct l1ctl_hdr *l1h;
+ struct l1ctl_info_ul *l1i_ul;
+ uint8_t chan_type, chan_ts, chan_ss;
+ uint8_t gsmtap_chan_type;
+
+ DEBUGP(DL1C, "(%s)\n", hexdump(msg->l2h, msgb_l2len(msg)));
+
+ if (msgb_l2len(msg) > 23) {
+ LOGP(DL1C, LOGL_ERROR, "L1 cannot handle message length "
+ "> 23 (%u)\n", msgb_l2len(msg));
+ msgb_free(msg);
+ return -EINVAL;
+ } else if (msgb_l2len(msg) < 23)
+ LOGP(DL1C, LOGL_ERROR, "L1 message length < 23 (%u) "
+ "doesn't seem right!\n", msgb_l2len(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_sendmsg(0|0x4000, 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));
+
+ l1i_ul->chan_nr = chan_nr;
+ l1i_ul->link_id = link_id;
+
+ /* FIXME: where to get this from? */
+ l1i_ul->tx_power = 0;
+
+ /* prepend l1 header */
+ msg->l1h = msgb_push(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = L1CTL_DATA_REQ;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit FBSB_REQ */
+int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn,
+ uint8_t flags, uint16_t timeout, uint8_t sync_info_idx,
+ uint8_t ccch_mode)
+{
+ struct msgb *msg;
+ struct l1ctl_fbsb_req *req;
+
+ msg = osmo_l1_alloc(L1CTL_FBSB_REQ);
+ if (!msg)
+ return -1;
+
+ req = (struct l1ctl_fbsb_req *) msgb_put(msg, sizeof(*req));
+ req->band_arfcn = htons(osmo_make_band_arfcn(ms, arfcn));
+ req->timeout = htons(timeout);
+ /* Threshold when to consider FB_MODE1: 4kHz - 1kHz */
+ req->freq_err_thresh1 = htons(4000 - 1000);
+ /* Threshold when to consider SCH: 1kHz - 200Hz */
+ req->freq_err_thresh2 = htons(1000 - 200);
+ /* not used yet! */
+ req->num_freqerr_avg = 3;
+ req->flags = flags;
+ req->sync_info_idx = sync_info_idx;
+ req->ccch_mode = ccch_mode;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_CCCH_MODE_REQ */
+int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode)
+{
+ struct msgb *msg;
+ struct l1ctl_ccch_mode_req *req;
+
+ msg = osmo_l1_alloc(L1CTL_CCCH_MODE_REQ);
+ if (!msg)
+ return -1;
+
+ req = (struct l1ctl_ccch_mode_req *) msgb_put(msg, sizeof(*req));
+ req->ccch_mode = ccch_mode;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_RACH_REQ */
+int tx_ph_rach_req(struct osmocom_ms *ms)
+{
+ struct msgb *msg;
+ struct l1ctl_info_ul *ul;
+ struct l1ctl_rach_req *req;
+ static uint8_t i = 0;
+
+ msg = osmo_l1_alloc(L1CTL_RACH_REQ);
+ if (!msg)
+ return -1;
+
+ DEBUGP(DL1C, "RACH Req.\n");
+ ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul));
+ req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req));
+ req->ra = i++;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_DM_EST_REQ */
+int tx_ph_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn,
+ uint8_t chan_nr, uint8_t tsc, uint8_t tx_power)
+{
+ struct msgb *msg;
+ struct l1ctl_info_ul *ul;
+ struct l1ctl_dm_est_req *req;
+
+ msg = osmo_l1_alloc(L1CTL_DM_EST_REQ);
+ if (!msg)
+ return -1;
+
+ DEBUGP(DL1C, "Tx Dedic.Mode Est Req (arfcn=%u, chan_nr=0x%02x)\n",
+ band_arfcn, chan_nr);
+
+ ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul));
+ ul->chan_nr = chan_nr;
+ ul->link_id = 0;
+ ul->tx_power = tx_power;
+
+ req = (struct l1ctl_dm_est_req *) msgb_put(msg, sizeof(*req));
+ req->tsc = tsc;
+ req->h = 0;
+ req->h0.band_arfcn = htons(band_arfcn);
+
+ return osmo_send_l1(ms, msg);
+}
+
+int tx_ph_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn,
+ uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc,
+ uint8_t tx_power)
+{
+ struct msgb *msg;
+ struct l1ctl_info_ul *ul;
+ struct l1ctl_dm_est_req *req;
+ int i;
+
+ msg = osmo_l1_alloc(L1CTL_DM_EST_REQ);
+ if (!msg)
+ return -1;
+
+ DEBUGP(DL1C, "Tx Dedic.Mode Est Req (maio=%u, hsn=%u, "
+ "chan_nr=0x%02x)\n", maio, hsn, chan_nr);
+
+ ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul));
+ ul->chan_nr = chan_nr;
+ ul->link_id = 0;
+ ul->tx_power = tx_power;
+
+ req = (struct l1ctl_dm_est_req *) msgb_put(msg, sizeof(*req));
+ req->tsc = tsc;
+ req->h = 1;
+ req->h1.maio = maio;
+ req->h1.hsn = hsn;
+ req->h1.n = ma_len;
+ for (i = 0; i < ma_len; i++)
+ req->h1.ma[i] = htons(ma[i]);
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_DM_REL_REQ */
+int tx_ph_dm_rel_req(struct osmocom_ms *ms)
+{
+ struct msgb *msg;
+ struct l1ctl_info_ul *ul;
+
+ msg = osmo_l1_alloc(L1CTL_DM_REL_REQ);
+ if (!msg)
+ return -1;
+
+ DEBUGP(DL1C, "Tx Dedic.Mode Rel Req\n");
+
+ ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul));
+
+ return osmo_send_l1(ms, msg);
+}
+
+int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len)
+{
+ struct msgb *msg;
+ uint8_t *data;
+ unsigned int i;
+
+ msg = osmo_l1_alloc(L1CTL_ECHO_REQ);
+ if (!msg)
+ return -1;
+
+ data = msgb_put(msg, len);
+ for (i = 0; i < len; i++)
+ data[i] = i % 8;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_PM_REQ */
+int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from,
+ uint16_t arfcn_to)
+{
+ struct msgb *msg;
+ struct l1ctl_pm_req *pm;
+
+ msg = osmo_l1_alloc(L1CTL_PM_REQ);
+ if (!msg)
+ return -1;
+
+ printf("Tx PM Req (%u-%u)\n", arfcn_from, arfcn_to);
+ pm = (struct l1ctl_pm_req *) msgb_put(msg, sizeof(*pm));
+ pm->type = 1;
+ pm->range.band_arfcn_from = htons(arfcn_from);
+ pm->range.band_arfcn_to = htons(arfcn_to);
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Transmit L1CTL_RESET_REQ */
+int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type)
+{
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = osmo_l1_alloc(L1CTL_RESET_REQ);
+ if (!msg)
+ return -1;
+
+ printf("Tx Reset Req (%u)\n", type);
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return osmo_send_l1(ms, msg);
+}
+
+/* Receive L1CTL_RESET_IND */
+static int rx_l1_reset(struct osmocom_ms *ms)
+{
+ printf("Layer1 Reset.\n");
+ dispatch_signal(SS_L1CTL, S_L1CTL_RESET, ms);
+
+ return 0;
+}
+
+/* Receive L1CTL_PM_CONF */
+static int rx_l1_pm_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct l1ctl_pm_conf *pmr;
+
+ for (pmr = (struct l1ctl_pm_conf *) msg->l1h;
+ (uint8_t *) pmr < msg->tail; pmr++) {
+ struct osmobb_meas_res mr;
+ DEBUGP(DL1C, "PM MEAS: ARFCN: %4u RxLev: %3d %3d\n",
+ ntohs(pmr->band_arfcn), pmr->pm[0], pmr->pm[1]);
+ mr.band_arfcn = ntohs(pmr->band_arfcn);
+ mr.rx_lev = (pmr->pm[0] + pmr->pm[1]) / 2;
+ mr.ms = ms;
+ dispatch_signal(SS_L1CTL, S_L1CTL_PM_RES, &mr);
+ }
+ return 0;
+}
+
+/* Receive L1CTL_MODE_CONF */
+static int rx_l1_ccch_mode_conf(struct osmocom_ms *ms, struct msgb *msg)
+{
+ struct osmobb_ccch_mode_conf mc;
+ struct l1ctl_ccch_mode_conf *conf;
+
+ if (msgb_l3len(msg) < sizeof(*conf)) {
+ LOGP(DL1C, LOGL_ERROR, "MODE CONF: MSG too short %u\n",
+ msgb_l3len(msg));
+ return -1;
+ }
+
+ conf = (struct l1ctl_ccch_mode_conf *) msg->l1h;
+
+ printf("mode=%u\n", conf->ccch_mode);
+
+ mc.ccch_mode = conf->ccch_mode;
+ mc.ms = ms;
+ dispatch_signal(SS_L1CTL, S_L1CTL_CCCH_MODE_CONF, &mc);
+
+ return 0;
+}
+
+/* Receive incoming data from L1 using L1CTL format */
+int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg)
+{
+ int rc = 0;
+ struct l1ctl_hdr *l1h;
+ struct l1ctl_info_dl *dl;
+
+ if (msgb_l2len(msg) < sizeof(*dl)) {
+ LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
+ msgb_l2len(msg));
+ msgb_free(msg);
+ return -1;
+ }
+
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+
+ /* move the l1 header pointer to point _BEHIND_ l1ctl_hdr,
+ as the l1ctl header is of no interest to subsequent code */
+ msg->l1h = l1h->data;
+
+ switch (l1h->msg_type) {
+ case L1CTL_FBSB_CONF:
+ rc = rx_l1_fbsb_conf(ms, msg);
+ msgb_free(msg);
+ break;
+ case L1CTL_DATA_IND:
+ rc = rx_ph_data_ind(ms, msg);
+ break;
+ case L1CTL_DATA_CONF:
+ rc = rx_ph_data_conf(ms, msg);
+ break;
+ case L1CTL_RESET_IND:
+ case L1CTL_RESET_CONF:
+ rc = rx_l1_reset(ms);
+ msgb_free(msg);
+ break;
+ case L1CTL_PM_CONF:
+ rc = rx_l1_pm_conf(ms, msg);
+ msgb_free(msg);
+ if (l1h->flags & L1CTL_F_DONE)
+ dispatch_signal(SS_L1CTL, S_L1CTL_PM_DONE, ms);
+ break;
+ case L1CTL_RACH_CONF:
+ rc = rx_l1_rach_conf(ms, msg);
+ msgb_free(msg);
+ break;
+ case L1CTL_CCCH_MODE_CONF:
+ rc = rx_l1_ccch_mode_conf(ms, msg);
+ msgb_free(msg);
+ break;
+ default:
+ fprintf(stderr, "Unknown MSG: %u\n", l1h->msg_type);
+ msgb_free(msg);
+ break;
+ }
+
+ return rc;
+}
diff --git a/src/host/layer23/src/l1l2_interface.c b/src/host/layer23/src/l1l2_interface.c
new file mode 100644
index 00000000..3c359e84
--- /dev/null
+++ b/src/host/layer23/src/l1l2_interface.c
@@ -0,0 +1,167 @@
+/* Layer 1 socket interface of layer2/3 stack */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (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 <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/logging.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <arpa/inet.h>
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define GSM_L2_LENGTH 256
+
+extern int quit;
+
+static int layer2_read(struct bsc_fd *fd)
+{
+ struct msgb *msg;
+ u_int16_t len;
+ int rc;
+
+ msg = msgb_alloc(GSM_L2_LENGTH, "Layer2");
+ if (!msg) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate msg.\n");
+ return -ENOMEM;
+ }
+
+ rc = read(fd->fd, &len, sizeof(len));
+ if (rc < sizeof(len)) {
+ fprintf(stderr, "Layer2 socket failed\n");
+ if (rc >= 0)
+ rc = -EIO;
+ quit = rc;
+ return rc;
+ }
+
+ len = ntohs(len);
+ if (len > GSM_L2_LENGTH) {
+ LOGP(DL1C, LOGL_ERROR, "Length is too big: %u\n", len);
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+
+ msg->l1h = msgb_put(msg, len);
+ rc = read(fd->fd, msg->l1h, msgb_l1len(msg));
+ if (rc != msgb_l1len(msg)) {
+ LOGP(DL1C, LOGL_ERROR, "Can not read data: len=%d rc=%d "
+ "errno=%d\n", len, rc, errno);
+ msgb_free(msg);
+ return rc;
+ }
+
+ l1ctl_recv((struct osmocom_ms *) fd->data, msg);
+
+ return 0;
+}
+
+static int layer2_write(struct bsc_fd *fd, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(fd->fd, msg->data, msg->len);
+ if (rc != msg->len) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to write data: rc: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int layer2_open(struct osmocom_ms *ms, const char *socket_path)
+{
+ int rc;
+ struct sockaddr_un local;
+
+ ms->wq.bfd.fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ms->wq.bfd.fd < 0) {
+ fprintf(stderr, "Failed to create unix domain socket.\n");
+ return ms->wq.bfd.fd;
+ }
+
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+ rc = connect(ms->wq.bfd.fd, (struct sockaddr *) &local,
+ sizeof(local.sun_family) + strlen(local.sun_path));
+ if (rc < 0) {
+ fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path);
+ close(ms->wq.bfd.fd);
+ return rc;
+ }
+
+ write_queue_init(&ms->wq, 100);
+ ms->wq.bfd.data = ms;
+ ms->wq.bfd.when = BSC_FD_READ;
+ ms->wq.read_cb = layer2_read;
+ ms->wq.write_cb = layer2_write;
+
+ rc = bsc_register_fd(&ms->wq.bfd);
+ if (rc != 0) {
+ fprintf(stderr, "Failed to register fd.\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+int layer2_close(struct osmocom_ms *ms)
+{
+ close(ms->wq.bfd.fd);
+ bsc_unregister_fd(&ms->wq.bfd);
+
+ return 0;
+}
+
+int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg)
+{
+ uint16_t *len;
+
+ DEBUGP(DL1C, "Sending: '%s'\n", hexdump(msg->data, msg->len));
+
+ if (msg->l1h != msg->data)
+ LOGP(DL1C, LOGL_ERROR, "Message L1 header != Message Data\n");
+
+ /* prepend 16bit length before sending */
+ len = (uint16_t *) msgb_push(msg, sizeof(*len));
+ *len = htons(msg->len - sizeof(*len));
+
+ if (write_queue_enqueue(&ms->wq, msg) != 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to enqueue msg.\n");
+ msgb_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+
diff --git a/src/host/layer23/src/lapdm.c b/src/host/layer23/src/lapdm.c
new file mode 100644
index 00000000..eff7aabe
--- /dev/null
+++ b/src/host/layer23/src/lapdm.c
@@ -0,0 +1,2126 @@
+/* GSM LAPDm (TS 04.06) implementation */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/* Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue
+ *
+ * RX data is stored in the rcv_buffer (pointer). If the message is complete, it
+ * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is
+ * received while there is an incomplete rcv_buffer, it is appended to it.
+ *
+ * TX data is stored in the send_queue first. When transmitting a frame,
+ * the first message in the send_queue is moved to the send_buffer. There it
+ * resides until all fragments are acknowledged. Fragments to be sent by I
+ * frames are stored in the tx_hist buffer for resend, if required. Also the
+ * current fragment is copied into the tx_queue. There it resides until it is
+ * forwarded to layer 1.
+ *
+ * In case we have SAPI 0, we only have a window size of 1, so the unack-
+ * nowledged message resides always in the send_buffer. In case of a suspend,
+ * it can be written back to the first position of the send_queue.
+ *
+ * The layer 1 normally sends a PH-READY-TO-SEND. But because we use
+ * asynchronous transfer between layer 1 and layer 2 (serial link), we must
+ * send a frame before layer 1 reaches the right timeslot to send it. So we
+ * move the tx_queue to layer 1 when there is not already a pending frame, and
+ * wait until acknowledge after the frame has been sent. If we receive an
+ * acknowledge, we can send the next frame from the buffer, if any.
+ *
+ * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it
+ * will trigger next I frame, if possible.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocore/logging.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/utils.h>
+#include <osmocore/rsl.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/protocol/gsm_08_58.h>
+
+#include <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/logging.h>
+
+#include <l1a_l23_interface.h>
+
+/* TS 04.06 Figure 4 / Section 3.2 */
+#define LAPDm_LPD_NORMAL 0
+#define LAPDm_LPD_SMSCB 1
+#define LAPDm_SAPI_NORMAL 0
+#define LAPDm_SAPI_SMS 3
+#define LAPDm_ADDR(lpd, sapi, cr) ((((lpd) & 0x3) << 5) | (((sapi) & 0x7) << 2) | (((cr) & 0x1) << 1) | 0x1)
+
+#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7)
+#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1)
+#define LAPDm_ADDR_EA(addr) ((addr) & 0x1)
+
+/* TS 04.06 Table 3 / Section 3.4.3 */
+#define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1))
+#define LAPDm_CTRL_S(nr, s, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((s) & 0x3) << 2) | 0x1)
+#define LAPDm_CTRL_U(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3)
+
+#define LAPDm_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0)
+#define LAPDm_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1)
+#define LAPDm_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3)
+
+#define LAPDm_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3)
+#define LAPDm_CTRL_PF_BIT(ctrl) (((ctrl) >> 4) & 0x1)
+
+#define LAPDm_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2)
+
+#define LAPDm_CTRL_I_Ns(ctrl) (((ctrl) & 0xE) >> 1)
+#define LAPDm_CTRL_Nr(ctrl) (((ctrl) & 0xE0) >> 5)
+
+/* TS 04.06 Table 4 / Section 3.8.1 */
+#define LAPDm_U_SABM 0x7
+#define LAPDm_U_DM 0x3
+#define LAPDm_U_UI 0x0
+#define LAPDm_U_DISC 0x8
+#define LAPDm_U_UA 0xC
+
+#define LAPDm_S_RR 0x0
+#define LAPDm_S_RNR 0x1
+#define LAPDm_S_REJ 0x2
+
+#define LAPDm_LEN(len) ((len << 2) | 0x1)
+#define LAPDm_MORE 0x2
+
+/* TS 04.06 Section 5.8.3 */
+#define N201_AB_SACCH 18
+#define N201_AB_SDCCH 20
+#define N201_AB_FACCH 20
+#define N201_Bbis 23
+#define N201_Bter_SACCH 21
+#define N201_Bter_SDCCH 23
+#define N201_Bter_FACCH 23
+#define N201_B4 19
+
+/* 5.8.2.1 N200 during establish and release */
+#define N200_EST_REL 5
+/* 5.8.2.1 N200 during timer recovery state */
+#define N200_TR_SACCH 5
+#define N200_TR_SDCCH 23
+#define N200_TR_FACCH_FR 34
+#define N200_TR_EFACCH_FR 48
+#define N200_TR_FACCH_HR 29
+/* FIXME: this depends on chan type */
+#define N200 N200_TR_SACCH
+
+#define CR_MS2BS_CMD 0
+#define CR_MS2BS_RESP 1
+#define CR_BS2MS_CMD 1
+#define CR_BS2MS_RESP 0
+
+/* Set T200 to 1 Second (OpenBTS uses 900ms) */
+#define T200 1, 0
+
+/* k value for each SAPI */
+static uint8_t k_sapi[] = {1, 1, 1, 1, 1, 1, 1, 1};
+
+enum lapdm_format {
+ LAPDm_FMT_A,
+ LAPDm_FMT_B,
+ LAPDm_FMT_Bbis,
+ LAPDm_FMT_Bter,
+ LAPDm_FMT_B4,
+};
+
+static void lapdm_t200_cb(void *data);
+static int rslms_send_i(struct lapdm_msg_ctx *mctx);
+
+/* UTILITY FUNCTIONS */
+
+static inline uint8_t inc_mod8(uint8_t x)
+{
+ return (x + 1) & 7;
+}
+
+static inline uint8_t add_mod8(uint8_t x, uint8_t y)
+{
+ return (x + y) & 7;
+}
+
+static inline uint8_t sub_mod8(uint8_t x, uint8_t y)
+{
+ return (x - y) & 7; /* handle negative results correctly */
+}
+
+static void lapdm_dl_init(struct lapdm_datalink *dl,
+ struct lapdm_entity *entity)
+{
+ memset(dl, 0, sizeof(*dl));
+ INIT_LLIST_HEAD(&dl->send_queue);
+ INIT_LLIST_HEAD(&dl->tx_queue);
+ dl->state = LAPDm_STATE_IDLE;
+ dl->t200.data = dl;
+ dl->t200.cb = &lapdm_t200_cb;
+ dl->entity = entity;
+}
+
+void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+ lapdm_dl_init(&le->datalink[i], le);
+
+ le->ms = ms;
+}
+
+static void lapdm_dl_flush_send(struct lapdm_datalink *dl)
+{
+ struct msgb *msg;
+
+ /* Flush send-queue */
+ while ((msg = msgb_dequeue(&dl->send_queue)))
+ msgb_free(msg);
+
+ /* Clear send-buffer */
+ if (dl->send_buffer) {
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = NULL;
+ }
+}
+
+static void lapdm_dl_flush_tx(struct lapdm_datalink *dl)
+{
+ struct msgb *msg;
+ unsigned int i;
+
+ while ((msg = msgb_dequeue(&dl->tx_queue)))
+ msgb_free(msg);
+ for (i = 0; i < 8; i++)
+ dl->tx_length[i] = 0;
+}
+
+void lapdm_exit(struct lapdm_entity *le)
+{
+ unsigned int i;
+ struct lapdm_datalink *dl;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ dl = &le->datalink[i];
+ lapdm_dl_flush_tx(dl);
+ lapdm_dl_flush_send(dl);
+ if (dl->rcv_buffer)
+ msgb_free(dl->rcv_buffer);
+ }
+}
+
+static void lapdm_dl_newstate(struct lapdm_datalink *dl, uint32_t state)
+{
+ LOGP(DLAPDM, LOGL_INFO, "new state %s -> %s\n",
+ lapdm_state_names[dl->state], lapdm_state_names[state]);
+
+ dl->state = state;
+}
+
+static struct lapdm_datalink *datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi)
+{
+ switch (sapi) {
+ case LAPDm_SAPI_NORMAL:
+ return &le->datalink[0];
+ case LAPDm_SAPI_SMS:
+ return &le->datalink[1];
+ default:
+ return NULL;
+ }
+}
+
+/* remove the L2 header from a MSGB */
+static inline unsigned char *msgb_pull_l2h(struct msgb *msg)
+{
+ unsigned char *ret = msgb_pull(msg, msg->l3h - msg->l2h);
+ msg->l2h = NULL;
+ return ret;
+}
+
+/* Append padding (if required) */
+static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201)
+{
+ int pad_len = n201 - msgb_l2len(msg);
+ uint8_t *data;
+
+ if (pad_len < 0) {
+ LOGP(DLAPDM, LOGL_ERROR,
+ "cannot pad message that is already too big!\n");
+ return;
+ }
+
+ data = msgb_put(msg, pad_len);
+ memset(data, 0x2B, pad_len);
+}
+
+/* write a frame into the tx queue */
+static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id, uint8_t n201)
+{
+ struct lapdm_entity *le = dl->entity;
+ struct osmocom_ms *ms = le->ms;
+
+ /* if there is a pending message, queue it */
+ if (le->tx_pending) {
+ *msgb_push(msg, 1) = n201;
+ *msgb_push(msg, 1) = link_id;
+ *msgb_push(msg, 1) = chan_nr;
+ msgb_enqueue(&dl->tx_queue, msg);
+ return -EBUSY;
+ }
+
+ /* send the frame now */
+ le->tx_pending = 1;
+#if 0
+printf("-> tx chan_nr 0x%x link_id 0x%x len %d data", chan_nr, link_id, msgb_l2len(msg));
+int i;
+for (i = 0; i < msgb_l2len(msg); i++)
+ printf(" %02x", msg->l2h[i]);
+printf("\n");
+#endif
+ lapdm_pad_msgb(msg, n201);
+ return tx_ph_data_req(ms, msg, chan_nr, link_id);
+}
+
+/* get next frame from the tx queue. because the ms has multiple datalinks,
+ * each datalink's queue is read round-robin.
+ */
+int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le)
+{
+ struct osmocom_ms *ms = le->ms;
+ struct lapdm_datalink *dl;
+ int last = le->last_tx_dequeue;
+ int i = last, n = ARRAY_SIZE(le->datalink);
+ uint8_t chan_nr, link_id, n201;
+
+ /* we may send again */
+ le->tx_pending = 0;
+
+#if 0
+printf("-> tx confirm\n");
+#endif
+ /* free confirm message */
+ msgb_free(msg);
+
+ /* round-robin dequeue */
+ do {
+ /* next */
+ i = (i + 1) % n;
+ dl = &le->datalink[i];
+ if ((msg = msgb_dequeue(&dl->tx_queue)))
+ break;
+ } while (i != last);
+
+ /* no message in all queues */
+ if (!msg)
+ return 0;
+
+ /* Pull chan_nr and link_id */
+ chan_nr = *msg->data;
+ msgb_pull(msg, 1);
+ link_id = *msg->data;
+ msgb_pull(msg, 1);
+ n201 = *msg->data;
+ msgb_pull(msg, 1);
+
+ /* Set last dequeue position */
+ le->last_tx_dequeue = i;
+
+ /* Pad the frame, we can transmit now */
+ le->tx_pending = 1;
+#if 0
+printf("-> more tx chan_nr 0x%x link_id 0x%x len %d data", chan_nr, link_id, msgb_l2len(msg));
+for (i = 0; i < msgb_l2len(msg); i++)
+ printf(" %02x", msg->l2h[i]);
+printf("\n");
+#endif
+ lapdm_pad_msgb(msg, n201);
+ return tx_ph_data_req(ms, msg, chan_nr, link_id);
+}
+
+/* Take a Bbis format message from L1 and create RSLms UNIT DATA IND */
+static int send_rslms_rll_l3(uint8_t msg_type, struct lapdm_msg_ctx *mctx,
+ struct msgb *msg)
+{
+ /* Add the RSL + RLL header */
+ rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, 1);
+
+ /* send off the RSLms message to L3 */
+ return rslms_sendmsg(msg, mctx->dl->entity->ms);
+}
+
+static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx)
+{
+ struct msgb *msg;
+
+ msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, 1);
+
+ /* send off the RSLms message to L3 */
+ return rslms_sendmsg(msg, mctx->dl->entity->ms);
+}
+
+static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx)
+{
+ struct msgb *msg;
+
+ LOGP(DLAPDM, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause);
+ msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 1);
+ msg->l2h = msgb_put(msg, sizeof(struct abis_rsl_rll_hdr) + 1);
+ msg->l2h[0] = cause;
+ return rslms_sendmsg(msg, mctx->dl->entity->ms);
+}
+
+static int check_length_ind(struct lapdm_msg_ctx *mctx, uint8_t length_ind)
+{
+ if (!(length_ind & 0x01)) {
+ /* G.4.1 If the EL bit is set to "0", an MDL-ERROR-INDICATION
+ * primitive with cause "frame not implemented" is sent to the
+ * mobile management entity. */
+ LOGP(DLAPDM, LOGL_NOTICE,
+ "we don't support multi-octet length\n");
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int lapdm_send_resend(struct lapdm_datalink *dl)
+{
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm resend");
+ int length;
+
+ /* Resend SABM/DISC from tx_hist */
+ length = dl->tx_length[0];
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, dl->tx_hist[dl->V_send], length);
+
+ return tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr, dl->mctx.link_id,
+ dl->mctx.n201);
+}
+
+static int lapdm_send_ua(struct lapdm_msg_ctx *mctx, uint8_t len, uint8_t *data)
+{
+ uint8_t sapi = mctx->link_id & 7;
+ uint8_t f_bit = LAPDm_CTRL_PF_BIT(mctx->ctrl);
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm UA");
+ msg->l2h = msgb_put(msg, 3 + len);
+
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UA, f_bit);
+ msg->l2h[2] = LAPDm_LEN(len);
+ if (len)
+ memcpy(msg->l2h + 3, data, len);
+
+ return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
+ mctx->n201);
+}
+
+static int lapdm_send_dm(struct lapdm_msg_ctx *mctx)
+{
+ uint8_t sapi = mctx->link_id & 7;
+ uint8_t f_bit = LAPDm_CTRL_PF_BIT(mctx->ctrl);
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm DM");
+ msg->l2h = msgb_put(msg, 3);
+
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_DM, f_bit);
+ msg->l2h[2] = 0;
+
+ return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
+ mctx->n201);
+}
+
+static int lapdm_send_rr(struct lapdm_msg_ctx *mctx, uint8_t f_bit)
+{
+ uint8_t sapi = mctx->link_id & 7;
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RR");
+ msg->l2h = msgb_put(msg, 3);
+
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP);
+ msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_RR, f_bit);
+ msg->l2h[2] = LAPDm_LEN(0);
+
+ return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
+ mctx->n201);
+}
+
+static int lapdm_send_rnr(struct lapdm_msg_ctx *mctx, uint8_t f_bit)
+{
+ uint8_t sapi = mctx->link_id & 7;
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RNR");
+ msg->l2h = msgb_put(msg, 3);
+
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP);
+ msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_RNR, f_bit);
+ msg->l2h[2] = LAPDm_LEN(0);
+
+ return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
+ mctx->n201);
+}
+
+static int lapdm_send_rej(struct lapdm_msg_ctx *mctx, uint8_t f_bit)
+{
+ uint8_t sapi = mctx->link_id & 7;
+ struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm REJ");
+ msg->l2h = msgb_put(msg, 3);
+
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP);
+ msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_REJ, f_bit);
+ msg->l2h[2] = LAPDm_LEN(0);
+
+ return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id,
+ mctx->n201);
+}
+
+/* Timer callback on T200 expiry */
+static void lapdm_t200_cb(void *data)
+{
+ struct lapdm_datalink *dl = data;
+
+ LOGP(DLAPDM, LOGL_INFO, "lapdm_t200_cb(%p) state=%u\n", dl, dl->state);
+
+ switch (dl->state) {
+ case LAPDm_STATE_SABM_SENT:
+ /* 5.4.1.3 */
+ if (dl->retrans_ctr + 1 >= N200_EST_REL + 1) {
+ /* send RELEASE INDICATION to L3 */
+ send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ /* send MDL ERROR INIDCATION to L3 */
+ rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx);
+ /* go back to idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ /* NOTE: we must not change any other states or buffers
+ * and queues, since we may reconnect after handover
+ * failure. the buffered messages is replaced there */
+ break;
+ }
+ /* retransmit SABM command */
+ lapdm_send_resend(dl);
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
+ /* restart T200 (PH-READY-TO-SEND) */
+ bsc_schedule_timer(&dl->t200, T200);
+ break;
+ case LAPDm_STATE_DISC_SENT:
+ /* 5.4.4.3 */
+ if (dl->retrans_ctr + 1 >= N200_EST_REL + 1) {
+ /* send RELEASE INDICATION to L3 */
+ send_rll_simple(RSL_MT_REL_CONF, &dl->mctx);
+ /* send MDL ERROR INIDCATION to L3 */
+ rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx);
+ /* go back to idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ /* NOTE: we must not change any other states or buffers
+ * and queues, since we may reconnect after handover
+ * failure. the buffered messages is replaced there */
+ break;
+ }
+ /* retransmit DISC command */
+ lapdm_send_resend(dl);
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
+ /* restart T200 (PH-READY-TO-SEND) */
+ bsc_schedule_timer(&dl->t200, T200);
+ break;
+ case LAPDm_STATE_MF_EST:
+ /* 5.5.7 */
+ dl->retrans_ctr = 0;
+ lapdm_dl_newstate(dl, LAPDm_STATE_TIMER_RECOV);
+ /* fall through */
+ case LAPDm_STATE_TIMER_RECOV:
+ dl->retrans_ctr++;
+ if (dl->retrans_ctr < N200) {
+ /* retransmit I frame (V_s-1) with P=1, if any */
+ if (dl->tx_length[dl->V_send - 1]) {
+ struct msgb *msg;
+ int length;
+
+ LOGP(DLAPDM, LOGL_INFO, "retransmit last frame "
+ "V(S)=%d\n", dl->V_send - 1);
+ /* Create I frame (segment) from tx_hist */
+ length = dl->tx_length[dl->V_send - 1];
+ msg = msgb_alloc_headroom(23+10, 10, "LAPDm I");
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, dl->tx_hist[dl->V_send - 1],
+ length);
+ msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv,
+ dl->V_send - 1, 1); /* P=1 */
+ tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr,
+ dl->mctx.link_id, dl->mctx.n201);
+ } else {
+ /* OR send appropriate supervision frame with P=1 */
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ lapdm_send_rr(&dl->mctx, 1);
+ /* NOTE: In case of sequence error
+ * condition, the REJ frame has been
+ * transmitted when entering the
+ * condition, so it has not be done
+ * here
+ */
+ } else if (dl->own_busy) {
+ lapdm_send_rnr(&dl->mctx, 1);
+ } else {
+ LOGP(DLAPDM, LOGL_INFO, "unhandled, "
+ "pls. fix\n");
+ }
+ }
+ /* restart T200 (PH-READY-TO-SEND) */
+ bsc_schedule_timer(&dl->t200, T200);
+ } else {
+ /* send MDL ERROR INIDCATION to L3 */
+ rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx);
+ }
+ break;
+ default:
+ LOGP(DLAPDM, LOGL_INFO, "T200 expired in unexpected "
+ "dl->state %u\n", dl->state);
+ }
+}
+
+/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */
+static void lapdm_acknowledge(struct lapdm_msg_ctx *mctx)
+{
+ struct lapdm_datalink *dl = mctx->dl;
+ uint8_t nr = LAPDm_CTRL_Nr(mctx->ctrl);
+ int s = 0, rej = 0, t200_reset = 0;
+ int i;
+
+ /* supervisory frame ? */
+ if (LAPDm_CTRL_is_S(mctx->ctrl))
+ s = 1;
+ /* REJ frame ? */
+ if (s && LAPDm_CTRL_S_BITS(mctx->ctrl) == LAPDm_S_REJ)
+ rej = 1;
+
+ /* Flush all transmit buffers of acknowledged frames */
+ for (i = dl->V_ack; i != nr; i = inc_mod8(i)) {
+ if (dl->tx_length[i]) {
+ dl->tx_length[i] = 0;
+ LOGP(DLAPDM, LOGL_INFO, "ack frame %d\n", i);
+ }
+ }
+
+ if (dl->state != LAPDm_STATE_TIMER_RECOV) {
+ /* When not in the timer recovery condition, the data
+ * link layer entity shall reset the timer T200 on
+ * receipt of a valid I frame with N(R) higher than V(A),
+ * or an REJ with an N(R) equal to V(A). */
+ if ((!rej && nr != dl->V_ack)
+ || (rej && nr == dl->V_ack)) {
+ LOGP(DLAPDM, LOGL_INFO, "reset t200\n");
+ t200_reset = 1;
+ bsc_del_timer(&dl->t200);
+ /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */
+ }
+ /* 5.7.4: N(R) sequence error
+ * N(R) is called valid, if and only if
+ * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8.
+ */
+ if (sub_mod8(nr, dl->V_ack) > sub_mod8(dl->V_send, dl->V_ack)) {
+ LOGP(DLAPDM, LOGL_NOTICE, "N(R) sequence error\n");
+ rsl_rll_error(RLL_CAUSE_SEQ_ERR, mctx);
+ }
+ }
+
+ /* V(A) shall be set to the value of N(R) */
+ dl->V_ack = nr;
+
+ /* If T200 has been reset by the receipt of an I, RR or RNR frame,
+ * and if there are outstanding I frames, restart T200 */
+ if (t200_reset && !rej) {
+ if (dl->tx_length[dl->V_send - 1]) {
+ LOGP(DLAPDM, LOGL_INFO, "start T200, due to unacked I "
+ "frame(s)\n");
+ bsc_schedule_timer(&dl->t200, T200);
+ }
+ }
+}
+
+/* L1 -> L2 */
+
+/* Receive a LAPDm U (Unnumbered) message from L1 */
+static int lapdm_rx_u(struct msgb *msg, struct lapdm_msg_ctx *mctx)
+{
+ struct lapdm_datalink *dl = mctx->dl;
+ uint8_t length;
+ int rc;
+ int rsl_msg;
+
+ switch (LAPDm_CTRL_U_BITS(mctx->ctrl)) {
+ case LAPDm_U_SABM:
+ rsl_msg = RSL_MT_EST_IND;
+
+ LOGP(DLAPDM, LOGL_INFO, "SABM received\n");
+ /* 5.7.1 */
+ dl->seq_err_cond = 0;
+ /* G.2.2 Wrong value of the C/R bit */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) {
+ LOGP(DLAPDM, LOGL_NOTICE, "SABM response error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+
+ length = msg->l2h[2] >> 2;
+ /* G.4.5 If SABM is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if ((msg->l2h[2] & LAPDm_MORE) || length + 3 > mctx->n201) {
+ LOGP(DLAPDM, LOGL_NOTICE, "SABM too large error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx);
+ return -EIO;
+ }
+
+ /* Must be Format B */
+ rc = check_length_ind(mctx, msg->l2h[2]);
+ if (rc < 0) {
+ msgb_free(msg);
+ return rc;
+ }
+ switch (dl->state) {
+ case LAPDm_STATE_IDLE:
+ /* Set chan_nr and link_id for established connection */
+ memset(&dl->mctx, 0, sizeof(dl->mctx));
+ dl->mctx.dl = dl;
+ dl->mctx.chan_nr = mctx->chan_nr;
+ dl->mctx.link_id = mctx->link_id;
+ break;
+ case LAPDm_STATE_MF_EST:
+ if (length == 0) {
+ rsl_msg = RSL_MT_EST_CONF;
+ break;
+ }
+ LOGP(DLAPDM, LOGL_INFO, "SABM command, multiple "
+ "frame established state\n");
+ /* check for contention resoultion */
+ if (dl->tx_hist[0][2] >> 2) {
+ LOGP(DLAPDM, LOGL_NOTICE, "SABM not allowed "
+ "during contention resolution\n");
+ rsl_rll_error(RLL_CAUSE_SABM_INFO_NOTALL, mctx);
+ }
+ msgb_free(msg);
+ return 0;
+ case LAPDm_STATE_DISC_SENT:
+ /* 5.4.6.2 send DM with F=P */
+ lapdm_send_dm(mctx);
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_CONF, mctx);
+ default:
+ lapdm_send_ua(mctx, length, msg->l2h + 3);
+ msgb_free(msg);
+ return 0;
+ }
+ /* send UA response */
+ lapdm_send_ua(mctx, length, msg->l2h + 3);
+ /* set Vs, Vr and Va to 0 */
+ dl->V_send = dl->V_recv = dl->V_ack = 0;
+ /* clear tx_hist */
+ dl->tx_length[0] = 0;
+ /* enter multiple-frame-established state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
+ /* send notification to L3 */
+ if (length == 0) {
+ /* 5.4.1.2 Normal establishment procedures */
+ rc = send_rll_simple(rsl_msg, mctx);
+ msgb_free(msg);
+ } else {
+ /* 5.4.1.4 Contention resolution establishment */
+ msg->l3h = msg->l2h + 3;
+ msgb_pull_l2h(msg);
+ rc = send_rslms_rll_l3(rsl_msg, mctx, msg);
+ }
+ break;
+ case LAPDm_U_DM:
+ LOGP(DLAPDM, LOGL_INFO, "DM received\n");
+ /* G.2.2 Wrong value of the C/R bit */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) {
+ LOGP(DLAPDM, LOGL_NOTICE, "DM command error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+ if (!LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ /* 5.4.1.2 DM responses with the F bit set to "0"
+ * shall be ignored.
+ */
+ msgb_free(msg);
+ return 0;
+ }
+ switch (dl->state) {
+ case LAPDm_STATE_SABM_SENT:
+ break;
+ case LAPDm_STATE_MF_EST:
+ if (LAPDm_CTRL_PF_BIT(mctx->ctrl) == 1) {
+ LOGP(DLAPDM, LOGL_INFO, "unsolicited DM "
+ "response\n");
+ rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP, mctx);
+ } else {
+ LOGP(DLAPDM, LOGL_INFO, "unsolicited DM "
+ "response, multiple frame established "
+ "state\n");
+ rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP_MF, mctx);
+ }
+ msgb_free(msg);
+ return 0;
+ case LAPDm_STATE_TIMER_RECOV:
+ /* DM is normal in case PF = 1 */
+ if (LAPDm_CTRL_PF_BIT(mctx->ctrl) == 0) {
+ LOGP(DLAPDM, LOGL_INFO, "unsolicited DM "
+ "response, multiple frame established "
+ "state\n");
+ rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP_MF, mctx);
+ msgb_free(msg);
+ return 0;
+ }
+ break;
+ case LAPDm_STATE_DISC_SENT:
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ /* go to idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ rc = send_rll_simple(RSL_MT_REL_CONF, mctx);
+ msgb_free(msg);
+ return 0;
+ case LAPDm_STATE_IDLE:
+ /* 5.4.5 all other frame types shall be discarded */
+ default:
+ LOGP(DLAPDM, LOGL_INFO, "unsolicited DM response! "
+ "(discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ /* reset T200 */
+ bsc_del_timer(&dl->t200);
+ rc = send_rll_simple(RSL_MT_REL_IND, mctx);
+ msgb_free(msg);
+ break;
+ case LAPDm_U_UI:
+ LOGP(DLAPDM, LOGL_INFO, "UI received\n");
+ /* G.2.2 Wrong value of the C/R bit */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) {
+ LOGP(DLAPDM, LOGL_NOTICE, "UI indicates response "
+ "error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+
+ length = msg->l2h[2] >> 2;
+ /* FIXME: G.4.5 If UI is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+
+ if (mctx->lapdm_fmt == LAPDm_FMT_B4) {
+ length = N201_B4;
+ msg->l3h = msg->l2h + 2;
+ } else {
+ rc = check_length_ind(mctx, msg->l2h[2]);
+ if (rc < 0) {
+ msgb_free(msg);
+ return rc;
+ }
+ length = msg->l2h[2] >> 2;
+ msg->l3h = msg->l2h + 3;
+ }
+ /* do some length checks */
+ if (length == 0) {
+ /* 5.3.3 UI frames received with the length indicator
+ * set to "0" shall be ignored
+ */
+ LOGP(DLAPDM, LOGL_INFO, "length=0 (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ switch (LAPDm_ADDR_SAPI(mctx->addr)) {
+ case LAPDm_SAPI_NORMAL:
+ case LAPDm_SAPI_SMS:
+ break;
+ default:
+ /* 5.3.3 UI frames with invalid SAPI values shall be
+ * discarded
+ */
+ LOGP(DLAPDM, LOGL_INFO, "sapi=%u (discarding)\n",
+ LAPDm_ADDR_SAPI(mctx->addr));
+ msgb_free(msg);
+ return 0;
+ }
+ msgb_pull_l2h(msg);
+ rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, mctx, msg);
+ break;
+ case LAPDm_U_DISC:
+ rsl_msg = RSL_MT_REL_IND;
+
+ LOGP(DLAPDM, LOGL_INFO, "DISC received\n");
+ /* flush buffers */
+ lapdm_dl_flush_tx(dl);
+ lapdm_dl_flush_send(dl);
+ /* 5.7.1 */
+ dl->seq_err_cond = 0;
+ /* G.2.2 Wrong value of the C/R bit */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) {
+ LOGP(DLAPDM, LOGL_NOTICE, "DISC response error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+ length = msg->l2h[2] >> 2;
+ if (length > 0 || msg->l2h[2] & 0x02) {
+ /* G.4.4 If a DISC or DM frame is received with L>0 or
+ * with the M bit set to "1", an MDL-ERROR-INDICATION
+ * primitive with cause "U frame with incorrect
+ * parameters" is sent to the mobile management entity. */
+ LOGP(DLAPDM, LOGL_NOTICE,
+ "U frame iwth incorrect parameters ");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx);
+ return -EIO;
+ }
+ msgb_free(msg);
+ switch (dl->state) {
+ case LAPDm_STATE_IDLE:
+ LOGP(DLAPDM, LOGL_INFO, "DISC in idle state\n");
+ /* send DM with F=P */
+ return lapdm_send_dm(mctx);
+ case LAPDm_STATE_SABM_SENT:
+ LOGP(DLAPDM, LOGL_INFO, "DISC in SABM state\n");
+ /* 5.4.6.2 send DM with F=P */
+ lapdm_send_dm(mctx);
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ return send_rll_simple(RSL_MT_REL_IND, mctx);
+ case LAPDm_STATE_MF_EST:
+ case LAPDm_STATE_TIMER_RECOV:
+ LOGP(DLAPDM, LOGL_INFO, "DISC in est state\n");
+ break;
+ case LAPDm_STATE_DISC_SENT:
+ LOGP(DLAPDM, LOGL_INFO, "DISC in disc state\n");
+ rsl_msg = RSL_MT_REL_CONF;
+ break;
+ default:
+ lapdm_send_ua(mctx, length, msg->l2h + 3);
+ return 0;
+ }
+ /* send UA response */
+ lapdm_send_ua(mctx, length, msg->l2h + 3);
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ /* enter idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ /* send notification to L3 */
+ rc = send_rll_simple(rsl_msg, mctx);
+ break;
+ case LAPDm_U_UA:
+ LOGP(DLAPDM, LOGL_INFO, "UA received\n");
+ /* G.2.2 Wrong value of the C/R bit */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) {
+ LOGP(DLAPDM, LOGL_NOTICE, "UA indicates command "
+ "error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+
+ length = msg->l2h[2] >> 2;
+ /* G.4.5 If UA is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if ((msg->l2h[2] & LAPDm_MORE) || length + 3 > mctx->n201) {
+ LOGP(DLAPDM, LOGL_NOTICE, "UA too large error\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx);
+ return -EIO;
+ }
+
+ if (!LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ /* 5.4.1.2 A UA response with the F bit set to "0"
+ * shall be ignored.
+ */
+ LOGP(DLAPDM, LOGL_INFO, "F=0 (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ switch (dl->state) {
+ case LAPDm_STATE_SABM_SENT:
+ break;
+ case LAPDm_STATE_MF_EST:
+ case LAPDm_STATE_TIMER_RECOV:
+ LOGP(DLAPDM, LOGL_INFO, "unsolicited UA response! "
+ "(discarding)\n");
+ rsl_rll_error(RLL_CAUSE_UNSOL_UA_RESP, mctx);
+ msgb_free(msg);
+ return 0;
+ case LAPDm_STATE_DISC_SENT:
+ LOGP(DLAPDM, LOGL_INFO, "UA in disconnect state\n");
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ /* go to idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ rc = send_rll_simple(RSL_MT_REL_CONF, mctx);
+ msgb_free(msg);
+ return 0;
+ case LAPDm_STATE_IDLE:
+ /* 5.4.5 all other frame types shall be discarded */
+ default:
+ LOGP(DLAPDM, LOGL_INFO, "unsolicited UA response! "
+ "(discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ LOGP(DLAPDM, LOGL_INFO, "UA in SABM state\n");
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ /* compare UA with SABME if contention resolution is applied */
+ if (dl->tx_hist[0][2] >> 2) {
+ rc = check_length_ind(mctx, msg->l2h[2]);
+ if (rc < 0) {
+ rc = send_rll_simple(RSL_MT_REL_IND, mctx);
+ msgb_free(msg);
+ /* go to idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ return 0;
+ }
+ length = msg->l2h[2] >> 2;
+ if (length != (dl->tx_hist[0][2] >> 2)
+ || !!memcmp(dl->tx_hist[0] + 3, msg->l2h + 3,
+ length)) {
+ LOGP(DLAPDM, LOGL_INFO, "UA response "
+ "mismatches\n");
+ rc = send_rll_simple(RSL_MT_REL_IND, mctx);
+ msgb_free(msg);
+ /* go to idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ return 0;
+ }
+ }
+ /* set Vs, Vr and Va to 0 */
+ dl->V_send = dl->V_recv = dl->V_ack = 0;
+ /* clear tx_hist */
+ dl->tx_length[0] = 0;
+ /* enter multiple-frame-established state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
+ /* send outstanding frames, if any (resume / reconnect) */
+ rslms_send_i(mctx);
+ /* send notification to L3 */
+ rc = send_rll_simple(RSL_MT_EST_CONF, mctx);
+ msgb_free(msg);
+ break;
+ default:
+ /* G.3.1 */
+ LOGP(DLAPDM, LOGL_NOTICE, "Unnumbered frame not allowed.\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+/* Receive a LAPDm S (Supervisory) message from L1 */
+static int lapdm_rx_s(struct msgb *msg, struct lapdm_msg_ctx *mctx)
+{
+ struct lapdm_datalink *dl = mctx->dl;
+ uint8_t length;
+
+ length = msg->l2h[2] >> 2;
+ if (length > 0 || msg->l2h[2] & 0x02) {
+ /* G.4.3 If a supervisory frame is received with L>0 or
+ * with the M bit set to "1", an MDL-ERROR-INDICATION
+ * primitive with cause "S frame with incorrect
+ * parameters" is sent to the mobile management entity. */
+ LOGP(DLAPDM, LOGL_NOTICE,
+ "S frame with incorrect parameters\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_SFRM_INC_PARAM, mctx);
+ return -EIO;
+ }
+ /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ LOGP(DLAPDM, LOGL_NOTICE, "S frame response with F=1 error\n");
+ rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx);
+ }
+
+ switch (dl->state) {
+ case LAPDm_STATE_IDLE:
+ /* if P=1, respond DM with F=1 (5.2.2) */
+ /* 5.4.5 all other frame types shall be discarded */
+ if (LAPDm_CTRL_PF_BIT(mctx->ctrl))
+ lapdm_send_dm(mctx); /* F=P */
+ /* fall though */
+ case LAPDm_STATE_SABM_SENT:
+ case LAPDm_STATE_DISC_SENT:
+ LOGP(DLAPDM, LOGL_NOTICE, "S frame ignored in this state\n");
+ msgb_free(msg);
+ return 0;
+ }
+ switch (LAPDm_CTRL_S_BITS(mctx->ctrl)) {
+ case LAPDm_S_RR:
+ LOGP(DLAPDM, LOGL_INFO, "RR received\n");
+ /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
+ lapdm_acknowledge(mctx);
+
+ /* 5.5.3.2 */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ LOGP(DLAPDM, LOGL_NOTICE, "RR frame command with polling bit set and we are not busy, so we reply with RR frame\n");
+ lapdm_send_rr(mctx, 1);
+ /* NOTE: In case of sequence error condition,
+ * the REJ frame has been transmitted when
+ * entering the condition, so it has not be
+ * done here
+ */
+ } else if (dl->own_busy) {
+ LOGP(DLAPDM, LOGL_NOTICE, "RR frame command with polling bit set and we are busy, so we reply with RR frame\n");
+ lapdm_send_rnr(mctx, 1);
+ }
+ }
+ /* Send message, if possible due to acknowledged data */
+ rslms_send_i(mctx);
+
+ break;
+ case LAPDm_S_RNR:
+ LOGP(DLAPDM, LOGL_INFO, "RNR received\n");
+ /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
+ lapdm_acknowledge(mctx);
+
+ /* 5.5.5 */
+ /* Set peer receiver busy condition */
+ dl->peer_busy = 1;
+
+ if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) {
+ if (!dl->own_busy) {
+ LOGP(DLAPDM, LOGL_INFO, "RNR poll "
+ "command and we are not busy, "
+ "so we reply with RR final "
+ "response\n");
+ /* Send RR with F=1 */
+ lapdm_send_rr(mctx, 1);
+ } else {
+ LOGP(DLAPDM, LOGL_INFO, "RNR poll "
+ "command and we are busy, so "
+ "we reply with RNR final "
+ "response\n");
+ /* Send RNR with F=1 */
+ lapdm_send_rnr(mctx, 1);
+ }
+ } else if (dl->state == LAPDm_STATE_TIMER_RECOV) {
+ LOGP(DLAPDM, LOGL_INFO, "RNR poll response "
+ "and we in timer recovery state, so "
+ "we leave that state\n");
+ /* Clear timer recovery condition */
+ lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
+ /* V(S) to the N(R) in the RNR frame */
+ dl->V_send = LAPDm_CTRL_Nr(mctx->ctrl);
+ }
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "RNR not polling/final state "
+ "received\n");
+
+ /* Send message, if possible due to acknowledged data */
+ rslms_send_i(mctx);
+
+ break;
+ case LAPDm_S_REJ:
+ LOGP(DLAPDM, LOGL_INFO, "REJ received\n");
+ /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
+ lapdm_acknowledge(mctx);
+
+ /* 5.5.4.1 */
+ if (dl->state != LAPDm_STATE_TIMER_RECOV) {
+ /* Clear an existing peer receiver busy condition */
+ dl->peer_busy = 0;
+ /* V(S) and V(A) to the N(R) in the REJ frame */
+ dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl);
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ /* 5.5.3.2 */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ LOGP(DLAPDM, LOGL_INFO, "REJ poll "
+ "command not in timer recovery "
+ "state and not in own busy "
+ "condition received, so we "
+ "respond with RR final "
+ "response\n");
+ lapdm_send_rr(mctx, 1);
+ /* NOTE: In case of sequence error
+ * condition, the REJ frame has been
+ * transmitted when entering the
+ * condition, so it has not be done
+ * here
+ */
+ } else if (dl->own_busy) {
+ LOGP(DLAPDM, LOGL_INFO, "REJ poll "
+ "command not in timer recovery "
+ "state and in own busy "
+ "condition received, so we "
+ "respond with RNR final "
+ "response\n");
+ lapdm_send_rnr(mctx, 1);
+ }
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "REJ response or not "
+ "polling command not in timer recovery "
+ "state received\n");
+ /* send MDL ERROR INIDCATION to L3 */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx);
+ }
+
+ } else if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ LOGP(DLAPDM, LOGL_INFO, "REJ poll response in timer "
+ "recovery state received\n");
+ /* Clear an existing peer receiver busy condition */
+ dl->peer_busy = 0;
+ /* Clear timer recovery condition */
+ lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST);
+ /* V(S) and V(A) to the N(R) in the REJ frame */
+ dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl);
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ } else {
+ /* Clear an existing peer receiver busy condition */
+ dl->peer_busy = 0;
+ /* V(S) and V(A) to the N(R) in the REJ frame */
+ dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl);
+ /* 5.5.3.2 */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD
+ && LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ if (!dl->own_busy && !dl->seq_err_cond) {
+ LOGP(DLAPDM, LOGL_INFO, "REJ poll "
+ "command in timer recovery "
+ "state and not in own busy "
+ "condition received, so we "
+ "respond with RR final "
+ "response\n");
+ lapdm_send_rr(mctx, 1);
+ /* NOTE: In case of sequence error
+ * condition, the REJ frame has been
+ * transmitted when entering the
+ * condition, so it has not be done
+ * here
+ */
+ } else if (dl->own_busy) {
+ LOGP(DLAPDM, LOGL_INFO, "REJ poll "
+ "command in timer recovery "
+ "state and in own busy "
+ "condition received, so we "
+ "respond with RNR final "
+ "response\n");
+ lapdm_send_rnr(mctx, 1);
+ }
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "REJ response or not "
+ "polling command in timer recovery "
+ "state received\n");
+ }
+
+ /* FIXME: 5.5.4.2 2) */
+
+ /* Send message, if possible due to acknowledged data */
+ rslms_send_i(mctx);
+
+ break;
+ default:
+ /* G.3.1 */
+ LOGP(DLAPDM, LOGL_NOTICE, "Supervisory frame not allowed.\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+ msgb_free(msg);
+ return 0;
+}
+
+/* Receive a LAPDm I (Information) message from L1 */
+static int lapdm_rx_i(struct msgb *msg, struct lapdm_msg_ctx *mctx)
+{
+ struct lapdm_datalink *dl = mctx->dl;
+ //uint8_t nr = LAPDm_CTRL_Nr(mctx->ctrl);
+ uint8_t ns = LAPDm_CTRL_I_Ns(mctx->ctrl);
+ uint8_t length;
+ int rc;
+
+ LOGP(DLAPDM, LOGL_NOTICE, "I received\n");
+
+ /* G.2.2 Wrong value of the C/R bit */
+ if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) {
+ LOGP(DLAPDM, LOGL_NOTICE, "I frame response not allowed\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+
+ length = msg->l2h[2] >> 2;
+ if (length == 0 || length + 3 > mctx->n201) {
+ /* G.4.2 If the length indicator of an I frame is set
+ * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION
+ * primitive with cause "I frame with incorrect length"
+ * is sent to the mobile management entity. */
+ LOGP(DLAPDM, LOGL_NOTICE, "I frame length not allowed\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_IFRM_INC_LEN, mctx);
+ return -EIO;
+ }
+
+ /* G.4.2 If the numerical value of L is L<N201 and the M
+ * bit is set to "1", then an MDL-ERROR-INDICATION primitive with
+ * cause "I frame with incorrect use of M bit" is sent to the
+ * mobile management entity. */
+ if ((msg->l2h[2] & LAPDm_MORE) && length + 3 < mctx->n201) {
+ LOGP(DLAPDM, LOGL_NOTICE, "I frame with M bit too short\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_IFRM_INC_MBITS, mctx);
+ return -EIO;
+ }
+
+ switch (dl->state) {
+ case LAPDm_STATE_IDLE:
+ /* if P=1, respond DM with F=1 (5.2.2) */
+ /* 5.4.5 all other frame types shall be discarded */
+ if (LAPDm_CTRL_PF_BIT(mctx->ctrl))
+ lapdm_send_dm(mctx); /* F=P */
+ /* fall though */
+ case LAPDm_STATE_SABM_SENT:
+ case LAPDm_STATE_DISC_SENT:
+ LOGP(DLAPDM, LOGL_NOTICE, "I frame ignored in this state\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ /* 5.7.1: N(s) sequence error */
+ if (ns != dl->V_recv) {
+ LOGP(DLAPDM, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, "
+ "V(R)=%u\n", ns, dl->V_recv);
+ /* discard data */
+ msgb_free(msg);
+ if (!dl->seq_err_cond) {
+ /* FIXME: help me understand what exactly todo here
+ dl->seq_err_cond = 1;
+ */
+ lapdm_send_rej(mctx, LAPDm_CTRL_PF_BIT(mctx->ctrl));
+ } else {
+ }
+ return -EIO;
+ }
+ dl->seq_err_cond = 0;
+
+ /* Increment receiver state */
+ dl->V_recv = inc_mod8(dl->V_recv);
+ LOGP(DLAPDM, LOGL_NOTICE, "incrementing V(R) to %u\n", dl->V_recv);
+
+ /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */
+ lapdm_acknowledge(mctx); /* V(A) is also set here */
+
+ /* Only if we are not in own receiver busy condition */
+ if (!dl->own_busy) {
+ /* if the frame carries a complete segment */
+ if (!(msg->l2h[2] & LAPDm_MORE)
+ && !dl->rcv_buffer) {
+ LOGP(DLAPDM, LOGL_INFO, "message in single I frame\n");
+ /* send a DATA INDICATION to L3 */
+ msg->l3h = msg->l2h + 3;
+ msgb_pull_l2h(msg);
+ rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx, msg);
+ } else {
+ /* create rcv_buffer */
+ if (!dl->rcv_buffer) {
+ LOGP(DLAPDM, LOGL_INFO, "message in multiple I "
+ "frames (first message)\n");
+ dl->rcv_buffer = msgb_alloc_headroom(200+10, 10,
+ "LAPDm RX");
+ dl->rcv_buffer->l3h = dl->rcv_buffer->data;
+ }
+ /* concat. rcv_buffer */
+ if (msgb_l3len(dl->rcv_buffer) + length > 200) {
+ LOGP(DLAPDM, LOGL_NOTICE, "Received frame "
+ "overflow!\n");
+ } else {
+ memcpy(msgb_put(dl->rcv_buffer, length),
+ msg->l2h + 3, length);
+ }
+ /* if the last segment was received */
+ if (!(msg->l2h[2] & LAPDm_MORE)) {
+ LOGP(DLAPDM, LOGL_INFO, "message in multiple I "
+ "frames (next message)\n");
+ rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx,
+ dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "message in multiple I "
+ "frames (last message)\n");
+ msgb_free(msg);
+
+ }
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "I frame ignored during own receiver "
+ "busy condition\n");
+
+ /* Check for P bit */
+ if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) {
+ /* 5.5.2.1 */
+ /* check if we are not in own receiver busy */
+ if (!dl->own_busy) {
+ LOGP(DLAPDM, LOGL_INFO, "we are not busy, send RR\n");
+ /* Send RR with F=1 */
+ rc = lapdm_send_rr(mctx, 1);
+ } else {
+ LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n");
+ /* Send RNR with F=1 */
+ rc = lapdm_send_rnr(mctx, 1);
+ }
+ } else {
+ /* 5.5.2.2 */
+ /* check if we are not in own receiver busy */
+ if (!dl->own_busy) {
+ /* NOTE: V(R) is already set above */
+ rc = rslms_send_i(mctx);
+ if (rc) {
+ LOGP(DLAPDM, LOGL_INFO, "we are not busy and "
+ "have no pending data, send RR\n");
+ /* Send RR with F=0 */
+ return lapdm_send_rr(mctx, 0);
+ }
+ } else {
+ LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n");
+ /* Send RNR with F=0 */
+ rc = lapdm_send_rnr(mctx, 0);
+ }
+ }
+
+ /* Send message, if possible due to acknowledged data */
+ rslms_send_i(mctx);
+
+ return rc;
+}
+
+/* Receive a LAPDm message from L1 */
+static int lapdm_ph_data_ind(struct msgb *msg, struct lapdm_msg_ctx *mctx)
+{
+ int rc;
+
+#if 0
+printf("-> rx chan_nr 0x%x link_id 0x%x len %d data", mctx->chan_nr, mctx->link_id, msgb_l2len(msg));
+int i;
+for (i = 0; i < msgb_l2len(msg); i++)
+ printf(" %02x", msg->l2h[i]);
+printf("\n");
+#endif
+ /* G.2.3 EA bit set to "0" is not allowed in GSM */
+ if (!LAPDm_ADDR_EA(mctx->addr)) {
+ LOGP(DLAPDM, LOGL_NOTICE, "EA bit 0 is not allowed in GSM\n");
+ msgb_free(msg);
+ rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx);
+ return -EINVAL;
+ }
+
+ if (LAPDm_CTRL_is_U(mctx->ctrl))
+ rc = lapdm_rx_u(msg, mctx);
+ else if (LAPDm_CTRL_is_S(mctx->ctrl))
+ rc = lapdm_rx_s(msg, mctx);
+ else if (LAPDm_CTRL_is_I(mctx->ctrl))
+ rc = lapdm_rx_i(msg, mctx);
+ else {
+ LOGP(DLAPDM, LOGL_NOTICE, "unknown LAPDm format\n");
+ msgb_free(msg);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/* input into layer2 (from layer 1) */
+int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i)
+{
+ uint8_t cbits = l1i->chan_nr >> 3;
+ uint8_t sapi = l1i->link_id & 7;
+ struct lapdm_msg_ctx mctx;
+ int rc = 0;
+
+ /* when we reach here, we have a msgb with l2h pointing to the raw
+ * 23byte mac block. The l1h has already been purged. */
+
+ mctx.dl = datalink_for_sapi(le, sapi);
+ mctx.chan_nr = l1i->chan_nr;
+ mctx.link_id = l1i->link_id;
+ mctx.addr = mctx.ctrl = 0;
+
+ /* G.2.1 No action schall be taken on frames containing an unallocated
+ * SAPI.
+ */
+ if (!mctx.dl) {
+ LOGP(DLAPDM, LOGL_NOTICE, "Received frame for unsupported "
+ "SAPI %d!\n", sapi);
+ return -EINVAL;
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ /* check for L1 chan_nr/link_id and determine LAPDm hdr format */
+ if (cbits == 0x10 || cbits == 0x12) {
+ /* Format Bbis is used on BCCH and CCCH(PCH, NCH and AGCH) */
+ mctx.lapdm_fmt = LAPDm_FMT_Bbis;
+ mctx.n201 = N201_Bbis;
+ } else {
+ if (mctx.link_id & 0x40) {
+ /* It was received from network on SACCH, thus
+ * lapdm_fmt must be B4 */
+ mctx.lapdm_fmt = LAPDm_FMT_B4;
+ mctx.n201 = N201_B4;
+ LOGP(DLAPDM, LOGL_INFO, "fmt=B4\n");
+ /* SACCH frames have a two-byte L1 header that
+ * OsmocomBB L1 doesn't strip */
+ msgb_pull(msg, 2);
+ msg->l2h += 2;
+ } else {
+ mctx.lapdm_fmt = LAPDm_FMT_B;
+ LOGP(DLAPDM, LOGL_INFO, "fmt=B\n");
+ mctx.n201 = 23; // FIXME: select correct size by chan.
+ }
+ }
+
+ switch (mctx.lapdm_fmt) {
+ case LAPDm_FMT_A:
+ case LAPDm_FMT_B:
+ case LAPDm_FMT_B4:
+ mctx.addr = msg->l2h[0];
+ if (!(mctx.addr & 0x01)) {
+ LOGP(DLAPDM, LOGL_ERROR, "we don't support "
+ "multibyte addresses (discarding)\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+ mctx.ctrl = msg->l2h[1];
+ /* obtain SAPI from address field */
+ mctx.link_id |= LAPDm_ADDR_SAPI(mctx.addr);
+ rc = lapdm_ph_data_ind(msg, &mctx);
+ break;
+ case LAPDm_FMT_Bter:
+ /* FIXME */
+ msgb_free(msg);
+ break;
+ case LAPDm_FMT_Bbis:
+ /* directly pass up to layer3 */
+ LOGP(DLAPDM, LOGL_INFO, "fmt=Bbis UI\n");
+ msg->l3h = msg->l2h;
+ msgb_pull_l2h(msg);
+ rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg);
+ break;
+ default:
+ msgb_free(msg);
+ }
+
+ return rc;
+}
+
+/* L3 -> L2 / RSLMS -> LAPDm */
+
+/* L3 requests establishment of data link */
+static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = rllh->link_id & 7;
+ struct tlv_parsed tv;
+ uint8_t length;
+ uint8_t n201 = 23; //FIXME
+
+ /* Set chan_nr and link_id for established connection */
+ memset(&dl->mctx, 0, sizeof(dl->mctx));
+ dl->mctx.dl = dl;
+ dl->mctx.n201 = n201;
+ dl->mctx.chan_nr = chan_nr;
+ dl->mctx.link_id = link_id;
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ /* contention resolution establishment procedure */
+ if (sapi != 0) {
+ /* According to clause 6, the contention resolution
+ * procedure is only permitted with SAPI value 0 */
+ LOGP(DLAPDM, LOGL_ERROR, "SAPI != 0 but contention"
+ "resolution (discarding)\n");
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ }
+ /* transmit a SABM command with the P bit set to "1". The SABM
+ * command shall contain the layer 3 message unit */
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+ LOGP(DLAPDM, LOGL_INFO, "perform establishment with content "
+ "(SAMB)\n");
+ } else {
+ /* normal establishment procedure */
+ length = 0;
+ LOGP(DLAPDM, LOGL_INFO, "perform normal establishm. (SAMB)\n");
+ }
+
+ /* check if the layer3 message length exceeds N201 */
+ if (length + 3 > 21) { /* FIXME: do we know the channel N201? */
+ LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) "
+ "(discarding)\n", length + 3, 21);
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ }
+
+ /* Flush send-queue */
+ /* Clear send-buffer */
+ lapdm_dl_flush_send(dl);
+
+ /* Discard partly received L3 message */
+ if (dl->rcv_buffer) {
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ }
+
+ /* Remove RLL header from msgb */
+ msgb_pull_l2h(msg);
+
+ /* Push LAPDm header on msgb */
+ msg->l2h = msgb_push(msg, 3);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_SABM, 1);
+ msg->l2h[2] = LAPDm_LEN(length);
+ /* Transmit-buffer carries exactly one segment */
+ memcpy(dl->tx_hist[0], msg->l2h, 3 + length);
+ dl->tx_length[0] = 3 + length;
+
+ /* Set states */
+ dl->own_busy = dl->peer_busy = 0;
+ dl->retrans_ctr = 0;
+ lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT);
+
+ /* Tramsmit and start T200 */
+ bsc_schedule_timer(&dl->t200, T200);
+ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201);
+}
+
+/* L3 requests transfer of unnumbered information */
+static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = link_id & 7;
+ struct tlv_parsed tv;
+ int length;
+ uint8_t n201 = 23; //FIXME
+
+ /* check if the layer3 message length exceeds N201 */
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+ /* check if the layer3 message length exceeds N201 */
+ if (length + 3 > 18) { /* FIXME: do we know the channel N201? */
+ LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) "
+ "(discarding)\n", length + 3, 18);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ LOGP(DLAPDM, LOGL_INFO, "sending unit data\n");
+
+ /* Remove RLL header from msgb */
+ msgb_pull_l2h(msg);
+
+ /* Push LAPDm header on msgb */
+ msg->l2h = msgb_push(msg, 3);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
+ msg->l2h[2] = LAPDm_LEN(length);
+ // FIXME: short L2 header support
+
+ /* Tramsmit */
+ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201);
+}
+
+/* L3 requests transfer of acknowledged information */
+static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct tlv_parsed tv;
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DLAPDM, LOGL_ERROR, "data request without message error\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ LOGP(DLAPDM, LOGL_INFO, "writing message to send-queue\n");
+
+ /* Remove the RSL/RLL header */
+ msgb_pull_l2h(msg);
+
+ /* Write data into the send queue */
+ msgb_enqueue(&dl->send_queue, msg);
+
+ /* Send message, if possible */
+ rslms_send_i(&dl->mctx);
+ return 0;
+}
+
+/* Send next I frame from queued/buffered data */
+static int rslms_send_i(struct lapdm_msg_ctx *mctx)
+{
+ struct lapdm_datalink *dl = mctx->dl;
+ uint8_t chan_nr = mctx->chan_nr;
+ uint8_t link_id = mctx->link_id;
+ uint8_t sapi = link_id & 7;
+ int k = k_sapi[sapi];
+ struct msgb *msg;
+ int length, left;
+ int rc = -1; /* we sent nothing */
+
+ next_frame:
+
+ if (dl->peer_busy) {
+ LOGP(DLAPDM, LOGL_INFO, "peer busy, not sending\n");
+ return rc;
+ }
+
+ if (dl->state == LAPDm_STATE_TIMER_RECOV) {
+ LOGP(DLAPDM, LOGL_INFO, "timer recovery, not sending\n");
+ return rc;
+ }
+
+ /* If the send state variable V(S) is equal to V(A) plus k
+ * (where k is the maximum number of outstanding I frames - see
+ * subclause 5.8.4), the data link layer entity shall not transmit any
+ * new I frames, but shall retransmit an I frame as a result
+ * of the error recovery procedures as described in subclauses 5.5.4 and
+ * 5.5.7. */
+ if (dl->V_send == add_mod8(dl->V_ack, k)) {
+ LOGP(DLAPDM, LOGL_INFO, "k frames outstanding, not sending "
+ "more (k=%u V(S)=%u V(A)=%u)\n", k, dl->V_send,
+ dl->V_ack);
+ return rc;
+ }
+
+ /* if we have no tx_hist yet, we create it */
+ if (!dl->tx_length[dl->V_send]) {
+ /* Get next message into send-buffer, if any */
+ if (!dl->send_buffer) {
+ next_message:
+ dl->send_out = 0;
+ dl->send_buffer = msgb_dequeue(&dl->send_queue);
+ /* No more data to be sent */
+ if (!dl->send_buffer)
+ return rc;
+ LOGP(DLAPDM, LOGL_INFO, "get message from "
+ "send-queue\n");
+ }
+
+ /* How much is left in the send-buffer? */
+ left = msgb_l3len(dl->send_buffer) - dl->send_out;
+ /* Segment, if data exceeds N201 */
+ length = left;
+ if (length > mctx->n201 - 3)
+ length = mctx->n201 - 3;
+printf("msg-len %d sent %d left %d N201 %d length %d first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left, mctx->n201, length, dl->send_buffer->l3h[0]);
+ /* If message in send-buffer is completely sent */
+ if (left == 0) {
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = NULL;
+ goto next_message;
+ }
+
+ LOGP(DLAPDM, LOGL_INFO, "send I frame %sV(S)=%d\n",
+ (left > length) ? "segment " : "", dl->V_send);
+
+ /* Create I frame (segment) and transmit-buffer content */
+ msg = msgb_alloc_headroom(23+10, 10, "LAPDm I");
+ msg->l2h = msgb_put(msg, 3 + length);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
+ msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, dl->V_send, 0);
+ msg->l2h[2] = LAPDm_LEN(length);
+ if (left > length)
+ msg->l2h[2] |= LAPDm_MORE;
+ memcpy(msg->l2h + 3, dl->send_buffer->l3h + dl->send_out,
+ length);
+ memcpy(dl->tx_hist[dl->V_send], msg->l2h, 3 + length);
+ dl->tx_length[dl->V_send] = 3 + length;
+ /* Add length to track how much is already in the tx buffer */
+ dl->send_out += length;
+ } else {
+ LOGP(DLAPDM, LOGL_INFO, "resend I frame from tx buffer "
+ "V(S)=%d\n", dl->V_send);
+
+ /* Create I frame (segment) from tx_hist */
+ length = dl->tx_length[dl->V_send];
+ msg = msgb_alloc_headroom(23+10, 10, "LAPDm I");
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, dl->tx_hist[dl->V_send], length);
+ msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, dl->V_send, 0);
+ }
+
+ /* The value of the send state variable V(S) shall be incremented by 1
+ * at the end of the transmission of the I frame */
+ dl->V_send = inc_mod8(dl->V_send);
+
+ /* If timer T200 is not running at the time right before transmitting a
+ * frame, when the PH-READY-TO-SEND primitive is received from the
+ * physical layer., it shall be set. */
+ if (!bsc_timer_pending(&dl->t200))
+ bsc_schedule_timer(&dl->t200, T200);
+
+ tx_ph_data_enqueue(dl, msg, chan_nr, link_id, mctx->n201);
+
+ rc = 0; /* we sent something */
+ goto next_frame;
+}
+
+/* L3 requests suspension of data link */
+static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t sapi = rllh->link_id & 7;
+
+ if (sapi != 0) {
+ LOGP(DLAPDM, LOGL_ERROR, "SAPI != 0 while suspending\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ LOGP(DLAPDM, LOGL_INFO, "perform suspension\n");
+
+ /* put back the send-buffer to the send-queue (first position) */
+ if (dl->send_buffer) {
+ llist_add(&dl->send_buffer->list, &dl->send_queue);
+ dl->send_buffer = NULL;
+ }
+
+ /* Clear transmit and send buffer, if any */
+ lapdm_dl_flush_tx(dl);
+ lapdm_dl_flush_send(dl);
+
+ msgb_free(msg);
+
+ return send_rll_simple(RSL_MT_SUSP_CONF, &dl->mctx);
+}
+
+/* L3 requests resume of data link */
+static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = rllh->link_id & 7;
+ struct tlv_parsed tv;
+ uint8_t length;
+ uint8_t n201 = 23; //FIXME
+
+ /* Set chan_nr and link_id for established connection */
+ memset(&dl->mctx, 0, sizeof(dl->mctx));
+ dl->mctx.dl = dl;
+ dl->mctx.n201 = n201;
+ dl->mctx.chan_nr = chan_nr;
+ dl->mctx.link_id = link_id;
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ LOGP(DLAPDM, LOGL_ERROR, "resume without message error\n");
+ msgb_free(msg);
+ return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
+ }
+ length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+
+ LOGP(DLAPDM, LOGL_INFO, "perform re-establishment (SAMB)\n");
+
+ /* Replace message in the send-buffer (reconnect) */
+ if (dl->send_buffer)
+ msgb_free(dl->send_buffer);
+ dl->send_buffer = msg;
+
+ /* Discard partly received L3 message */
+ if (dl->rcv_buffer) {
+ msgb_free(dl->rcv_buffer);
+ dl->rcv_buffer = NULL;
+ }
+
+ /* Create new msgb (old one is now free) */
+ msg = msgb_alloc_headroom(23+10, 10, "LAPDm SABM");
+ msg->l2h = msgb_put(msg, 3);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_SABM, 1);
+ msg->l2h[2] = LAPDm_LEN(0);
+ /* Transmit-buffer carries exactly one segment */
+ memcpy(dl->tx_hist[0], msg->l2h, 3);
+ dl->tx_length[0] = 3;
+
+ /* Set states */
+ dl->own_busy = dl->peer_busy = 0;
+ dl->retrans_ctr = 0;
+ lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT);
+
+ /* Tramsmit and start T200 */
+ bsc_schedule_timer(&dl->t200, T200);
+ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201);
+}
+
+/* L3 requests release of data link */
+static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ uint8_t chan_nr = rllh->chan_nr;
+ uint8_t link_id = rllh->link_id;
+ uint8_t sapi = rllh->link_id & 7;
+ uint8_t mode = 0;
+
+ /* get release mode */
+ if (rllh->data[0] == RSL_IE_RELEASE_MODE)
+ mode = rllh->data[1] & 1;
+
+ /* local release */
+ if (mode) {
+ LOGP(DLAPDM, LOGL_INFO, "perform local release\n");
+ msgb_free(msg);
+ /* reset Timer T200 */
+ bsc_del_timer(&dl->t200);
+ /* enter idle state */
+ lapdm_dl_newstate(dl, LAPDm_STATE_IDLE);
+ /* send notification to L3 */
+ return send_rll_simple(RSL_MT_REL_CONF, &dl->mctx);
+ } else
+ LOGP(DLAPDM, LOGL_INFO, "perform normal release (DISC)\n");
+
+ /* Create new msgb */
+ msgb_pull_l2h(msg);
+
+ /* Push LAPDm header on msgb */
+ msg->l2h = msgb_push(msg, 3);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_DISC, 1);
+ msg->l2h[2] = LAPDm_LEN(0);
+ /* Transmit-buffer carries exactly one segment */
+ memcpy(dl->tx_hist[0], msg->l2h, 3);
+ dl->tx_length[0] = 3;
+
+ /* Set states */
+ dl->own_busy = dl->peer_busy = 0;
+ dl->retrans_ctr = 0;
+ lapdm_dl_newstate(dl, LAPDm_STATE_DISC_SENT);
+
+ /* Tramsmit and start T200 */
+ bsc_schedule_timer(&dl->t200, T200);
+ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, dl->mctx.n201);
+}
+
+/* L3 requests release in idle state */
+static int rslms_rx_rll_rel_req_idle(struct msgb *msg, struct lapdm_datalink *dl)
+{
+ msgb_free(msg);
+
+ /* send notification to L3 */
+ return send_rll_simple(RSL_MT_REL_CONF, &dl->mctx);
+}
+
+/* Names for Radio Link Layer Management */
+static const struct value_string rsl_msg_names[] = {
+ { RSL_MT_DATA_REQ, "RSL_MT_DATA_REQ" },
+ { RSL_MT_DATA_IND, "RSL_MT_DATA_IND" },
+ { RSL_MT_ERROR_IND, "RSL_MT_ERROR_IND" },
+ { RSL_MT_EST_REQ, "RSL_MT_EST_REQ" },
+ { RSL_MT_EST_CONF, "RSL_MT_EST_CONF" },
+ { RSL_MT_EST_IND, "RSL_MT_EST_IND" },
+ { RSL_MT_EST_IND, "RSL_MT_REL_REQ" },
+ { RSL_MT_REL_REQ, "RSL_MT_REL_REQ" },
+ { RSL_MT_REL_CONF, "RSL_MT_REL_CONF" },
+ { RSL_MT_REL_IND, "RSL_MT_REL_IND" },
+ { RSL_MT_UNIT_DATA_REQ, "RSL_MT_UNIT_DATA_REQ" },
+ { RSL_MT_UNIT_DATA_IND, "RSL_MT_UNIT_DATA_IND" },
+ { RSL_MT_SUSP_REQ, "RSL_MT_SUSP_REQ" },
+ { RSL_MT_SUSP_CONF, "RSL_MT_SUSP_CONF" },
+ { RSL_MT_RES_REQ, "RSL_MT_RES_REQ" },
+ { RSL_MT_RECON_REQ, "RSL_MT_RECON_REQ" },
+ { 0, NULL }
+};
+
+const char *get_rsl_name(int value)
+{
+ return get_value_string(rsl_msg_names, value);
+}
+
+const char *lapdm_state_names[] = {
+ "LAPDm_STATE_NULL",
+ "LAPDm_STATE_IDLE",
+ "LAPDm_STATE_SABM_SENT",
+ "LAPDm_STATE_MF_EST",
+ "LAPDm_STATE_TIMER_RECOV",
+ "LAPDm_STATE_DISC_SENT",
+};
+
+/* statefull handling for RSLms RLL messages from L3 */
+static struct l2downstate {
+ uint32_t states;
+ int type;
+ int (*rout) (struct msgb *msg, struct lapdm_datalink *dl);
+} l2downstatelist[] = {
+ /* create and send UI command */
+ {ALL_STATES,
+ RSL_MT_UNIT_DATA_REQ, rslms_rx_rll_udata_req},
+
+ /* create and send SABM command */
+ {SBIT(LAPDm_STATE_IDLE),
+ RSL_MT_EST_REQ, rslms_rx_rll_est_req},
+
+ /* create and send I command */
+ {SBIT(LAPDm_STATE_MF_EST) |
+ SBIT(LAPDm_STATE_TIMER_RECOV),
+ RSL_MT_DATA_REQ, rslms_rx_rll_data_req},
+
+ /* suspend datalink */
+ {SBIT(LAPDm_STATE_MF_EST) |
+ SBIT(LAPDm_STATE_TIMER_RECOV),
+ RSL_MT_SUSP_REQ, rslms_rx_rll_susp_req},
+
+ /* create and send SABM command (resume) */
+ {SBIT(LAPDm_STATE_MF_EST) |
+ SBIT(LAPDm_STATE_TIMER_RECOV),
+ RSL_MT_RES_REQ, rslms_rx_rll_res_req},
+
+ /* create and send SABM command (reconnect) */
+ {SBIT(LAPDm_STATE_MF_EST) |
+ SBIT(LAPDm_STATE_TIMER_RECOV),
+ RSL_MT_RECON_REQ, rslms_rx_rll_res_req},
+
+ /* create and send DISC command */
+ {SBIT(LAPDm_STATE_SABM_SENT) |
+ SBIT(LAPDm_STATE_MF_EST) |
+ SBIT(LAPDm_STATE_TIMER_RECOV),
+ RSL_MT_REL_REQ, rslms_rx_rll_rel_req},
+
+ /* release in idle state */
+ {SBIT(LAPDm_STATE_IDLE),
+ RSL_MT_REL_REQ, rslms_rx_rll_rel_req_idle},
+
+ /* FIXME: create and send DISC command */
+};
+
+#define L2DOWNSLLEN \
+ (sizeof(l2downstatelist) / sizeof(struct l2downstate))
+
+/* incoming RSLms RLL message from L3 */
+static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int msg_type = rllh->c.msg_type;
+ uint8_t sapi = rllh->link_id & 7;
+ struct lapdm_entity *le;
+ struct lapdm_datalink *dl;
+ int i, supported = 0;
+ int rc = 0;
+
+ if (rllh->link_id & 0x40)
+ le = &ms->l2_entity.lapdm_acch;
+ else
+ le = &ms->l2_entity.lapdm_dcch;
+
+ /* G.2.1 No action schall be taken on frames containing an unallocated
+ * SAPI.
+ */
+ dl = datalink_for_sapi(le, sapi);
+ if (!dl) {
+ LOGP(DRSL, LOGL_ERROR, "No instance for SAPI %d!\n", sapi);
+ return -EINVAL;
+ }
+
+ LOGP(DRSL, LOGL_INFO, "(ms %s) RLL Message '%s' received in state %s\n",
+ ms->name, get_rsl_name(msg_type), lapdm_state_names[dl->state]);
+
+ /* find function for current state and message */
+ for (i = 0; i < L2DOWNSLLEN; i++) {
+ if (msg_type == l2downstatelist[i].type)
+ supported = 1;
+ if ((msg_type == l2downstatelist[i].type)
+ && ((1 << dl->state) & l2downstatelist[i].states))
+ break;
+ }
+ if (!supported) {
+ LOGP(DRSL, LOGL_NOTICE, "Message unsupported.\n");
+ msgb_free(msg);
+ return 0;
+ }
+ if (i == L2DOWNSLLEN) {
+ LOGP(DRSL, LOGL_NOTICE, "Message unhandled at this state.\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ rc = l2downstatelist[i].rout(msg, dl);
+
+ return rc;
+}
+
+/* input into layer2 (from layer 3) */
+int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = rslms_rx_rll(msg, ms);
+ break;
+ default:
+ LOGP(DRSL, LOGL_ERROR, "unknown RSLms message "
+ "discriminator 0x%02x", rslh->msg_discr);
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+/* input function that L2 calls when sending messages up to L3 */
+int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms)
+{
+ if (!ms->l2_entity.msg_handler) {
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ /* call the layer2 message handler that is registered */
+ return ms->l2_entity.msg_handler(msg, ms);
+}
+
+/* register message handler for messages that are sent from L2->L3 */
+int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb)
+{
+ ms->l2_entity.msg_handler = cb;
+
+ return 0;
+}
diff --git a/src/host/layer23/src/layer3.c b/src/host/layer23/src/layer3.c
new file mode 100644
index 00000000..978ea104
--- /dev/null
+++ b/src/host/layer23/src/layer3.c
@@ -0,0 +1,235 @@
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/rsl.h>
+#include <osmocore/tlv.h>
+#include <osmocore/protocol/gsm_04_08.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/rslms.h>
+#include <osmocom/layer3.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+
+static int ccch_mode = CCCH_MODE_NONE;
+
+static void dump_bcch(struct osmocom_ms *ms, uint8_t tc, const uint8_t *data)
+{
+ struct gsm48_system_information_type_header *si_hdr;
+ si_hdr = (struct gsm48_system_information_type_header *) data;
+
+ /* GSM 05.02 §6.3.1.3 Mapping of BCCH data */
+ switch (si_hdr->system_information) {
+ case GSM48_MT_RR_SYSINFO_1:
+ fprintf(stderr, "\tSI1");
+#ifdef BCCH_TC_CHECK
+ if (tc != 0)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2:
+ fprintf(stderr, "\tSI2");
+#ifdef BCCH_TC_CHECK
+ if (tc != 1)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_3:
+ fprintf(stderr, "\tSI3");
+#ifdef BCCH_TC_CHECK
+ if (tc != 2 && tc != 6)
+ fprintf(stderr, " on wrong TC");
+#endif
+ if (ccch_mode == CCCH_MODE_NONE) {
+ struct gsm48_system_information_type_3 *si3 =
+ (struct gsm48_system_information_type_3 *)data;
+
+ if (si3->control_channel_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
+ ccch_mode = CCCH_MODE_COMBINED;
+ else
+ ccch_mode = CCCH_MODE_NON_COMBINED;
+
+ l1ctl_tx_ccch_mode_req(ms, ccch_mode);
+ }
+ break;
+ case GSM48_MT_RR_SYSINFO_4:
+ fprintf(stderr, "\tSI4");
+#ifdef BCCH_TC_CHECK
+ if (tc != 3 && tc != 7)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_5:
+ fprintf(stderr, "\tSI5");
+ break;
+ case GSM48_MT_RR_SYSINFO_6:
+ fprintf(stderr, "\tSI6");
+ break;
+ case GSM48_MT_RR_SYSINFO_7:
+ fprintf(stderr, "\tSI7");
+#ifdef BCCH_TC_CHECK
+ if (tc != 7)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_8:
+ fprintf(stderr, "\tSI8");
+#ifdef BCCH_TC_CHECK
+ if (tc != 3)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_9:
+ fprintf(stderr, "\tSI9");
+#ifdef BCCH_TC_CHECK
+ if (tc != 4)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_13:
+ fprintf(stderr, "\tSI13");
+#ifdef BCCH_TC_CHECK
+ if (tc != 4 && tc != 0)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_16:
+ fprintf(stderr, "\tSI16");
+#ifdef BCCH_TC_CHECK
+ if (tc != 6)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_17:
+ fprintf(stderr, "\tSI17");
+#ifdef BCCH_TC_CHECK
+ if (tc != 2)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2bis:
+ fprintf(stderr, "\tSI2bis");
+#ifdef BCCH_TC_CHECK
+ if (tc != 5)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_2ter:
+ fprintf(stderr, "\tSI2ter");
+#ifdef BCCH_TC_CHECK
+ if (tc != 5 && tc != 4)
+ fprintf(stderr, " on wrong TC");
+#endif
+ break;
+ case GSM48_MT_RR_SYSINFO_5bis:
+ fprintf(stderr, "\tSI5bis");
+ break;
+ case GSM48_MT_RR_SYSINFO_5ter:
+ fprintf(stderr, "\tSI5ter");
+ break;
+ default:
+ fprintf(stderr, "\tUnknown SI");
+ break;
+ };
+
+ fprintf(stderr, "\n");
+}
+
+
+/* send location updating request * (as part of RSLms EST IND /
+ LAPDm SABME) */
+static int gsm48_tx_loc_upd_req(struct osmocom_ms *ms, uint8_t chan_nr)
+{
+ struct msgb *msg = msgb_alloc_headroom(256, 16, "loc_upd_req");
+ struct gsm48_hdr *gh;
+ struct gsm48_loc_upd_req *lu_r;
+
+ DEBUGP(DMM, "chan_nr=%u\n", chan_nr);
+
+ msg->l3h = msgb_put(msg, sizeof(*gh));
+ gh = (struct gsm48_hdr *) msg->l3h;
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST;
+ lu_r = (struct gsm48_loc_upd_req *) msgb_put(msg, sizeof(*lu_r));
+ lu_r->type = GSM48_LUPD_IMSI_ATT;
+ lu_r->key_seq = 0;
+ /* FIXME: set LAI and CM1 */
+ /* FIXME: set MI */
+ lu_r->mi_len = 0;
+
+ return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, chan_nr, 0, msg);
+}
+
+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;
+ uint16_t arfcn;
+
+ rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts);
+ arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8);
+
+ DEBUGP(DRR, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, "
+ "ARFCN=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra,
+ ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch,
+ ia->chan_desc.h0.tsc);
+
+ /* FIXME: compare RA and GSM time with when we sent RACH req */
+
+ /* check if we can support this type of channel at the moment */
+ if (ch_type != RSL_CHAN_SDCCH4_ACCH || ch_ts != 0 ||
+ ia->chan_desc.h0.h == 1) {
+ DEBUGPC(DRR, "UNSUPPORTED!\n");
+ return 0;
+ }
+
+ /* request L1 to go to dedicated mode on assigned channel */
+ tx_ph_dm_est_req_h0(ms, arfcn, ia->chan_desc.chan_nr,
+ ia->chan_desc.h0.tsc, 0);
+
+ /* request L2 to establish the SAPI0 connection */
+ gsm48_tx_loc_upd_req(ms, ia->chan_desc.chan_nr);
+
+ DEBUGPC(DRR, "\n");
+
+ return 0;
+}
+
+int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct gsm48_system_information_type_header *sih = msgb_l3(msg);
+ int rc = 0;
+
+ if (sih->rr_protocol_discriminator != GSM48_PDISC_RR)
+ LOGP(DRR, LOGL_ERROR, "PCH pdisc != RR\n");
+
+ switch (sih->system_information) {
+ case GSM48_MT_RR_PAG_REQ_1:
+ case GSM48_MT_RR_PAG_REQ_2:
+ case GSM48_MT_RR_PAG_REQ_3:
+ /* FIXME: implement decoding of paging request */
+ break;
+ case GSM48_MT_RR_IMM_ASS:
+ rc = gsm48_rx_imm_ass(msg, ms);
+ break;
+ default:
+ LOGP(DRR, LOGL_NOTICE, "unknown PCH/AGCH type 0x%02x\n",
+ sih->system_information);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms)
+{
+ /* FIXME: we have lost the gsm frame time until here, need to store it
+ * in some msgb context */
+ //dump_bcch(dl->time.tc, ccch->data);
+ dump_bcch(ms, 0, msg->l3h);
+
+ return 0;
+}
diff --git a/src/host/layer23/src/logging.c b/src/host/layer23/src/logging.c
new file mode 100644
index 00000000..82195327
--- /dev/null
+++ b/src/host/layer23/src/logging.c
@@ -0,0 +1,112 @@
+/* Logging/Debug support of the layer2/3 stack */
+
+/* (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 <osmocore/utils.h>
+#include <osmocore/logging.h>
+#include <osmocom/logging.h>
+
+static const struct log_info_cat default_categories[] = {
+ [DRSL] = {
+ .name = "DRSL",
+ .description = "Radio Signalling Link (MS)",
+ .color = "\033[1;35m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DCS] = {
+ .name = "DCS",
+ .description = "Cell selection",
+ .color = "\033[34m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DPLMN] = {
+ .name = "DPLMN",
+ .description = "PLMN selection",
+ .color = "\033[32m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DRR] = {
+ .name = "DRR",
+ .description = "Radio Resource",
+ .color = "\033[1;34m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DMM] = {
+ .name = "DMM",
+ .description = "Mobility Management",
+ .color = "\033[1;32m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DCC] = {
+ .name = "DCC",
+ .description = "Call Control",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DSMS] = {
+ .name = "DSMS",
+ .description = "Short Message Service",
+ .color = "\033[1;37m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DMNCC] = {
+ .name = "DMNCC",
+ .description = "Mobile Network Call Control",
+ .color = "\033[1;37m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DMEAS] = {
+ .name = "DMEAS",
+ .description = "MEasurement Reporting",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DPAG] = {
+ .name = "DPAG",
+ .description = "Paging",
+ .color = "\033[33m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DLAPDM] = {
+ .name = "DLAPDM",
+ .description = "LAPDm Layer2",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DL1C] = {
+ .name = "DL1C",
+ .description = "Layer 1 Control",
+ .color = "\033[1;31m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+ [DSUM] = {
+ .name = "DSUM",
+ .description = "Summary of Process",
+ .color = "\033[1;37m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+};
+
+const struct log_info log_info = {
+ .filter_fn = NULL,
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
diff --git a/src/host/layer23/src/main.c b/src/host/layer23/src/main.c
new file mode 100644
index 00000000..1816e150
--- /dev/null
+++ b/src/host/layer23/src/main.c
@@ -0,0 +1,212 @@
+/* Main method of the layer2/3 stack */
+
+/* (C) 2010 by Holger Hans Peter Freyther
+ * (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 <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+#include <osmocom/l1l2_interface.h>
+#include <osmocom/layer3.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/gsmtap_util.h>
+#include <osmocom/logging.h>
+#include <osmocom/l23_app.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/select.h>
+#include <osmocore/linuxlist.h>
+
+#include <arpa/inet.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+struct log_target *stderr_target;
+
+void *l23_ctx = NULL;
+static char *socket_path = "/tmp/osmocom_l2";
+struct llist_head ms_list;
+static struct osmocom_ms *ms = NULL;
+static uint32_t gsmtap_ip = 0;
+unsigned short vty_port = 4247;
+int (*l23_app_work) (struct osmocom_ms *ms) = NULL;
+int (*l23_app_exit) (struct osmocom_ms *ms) = NULL;
+int quit = 0;
+
+const char *openbsc_copyright =
+ "Copyright (C) 2008-2010 ...\n"
+ "Contributions by ...\n\n"
+ "License GPLv2+: GNU GPL version 2 or later "
+ "<http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n";
+
+static void print_usage(const char *app)
+{
+ printf("Usage: %s\n", app);
+}
+
+static void print_help()
+{
+ printf(" Some help...\n");
+ printf(" -h --help this text\n");
+ printf(" -s --socket /tmp/osmocom_l2. Path to the unix "
+ "domain socket\n");
+ printf(" -a --arfcn NR The ARFCN to be used for layer2.\n");
+ printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n");
+ printf(" -v --vty-port The VTY port number to telnet to. "
+ "(default %u)\n", vty_port);
+ printf(" -d --debug Change debug flags.\n");
+}
+
+static void handle_options(int argc, char **argv)
+{
+ struct sockaddr_in gsmtap;
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"socket", 1, 0, 's'},
+ {"arfcn", 1, 0, 'a'},
+ {"gsmtap-ip", 1, 0, 'i'},
+ {"vty-port", 1, 0, 'v'},
+ {"debug", 1, 0, 'd'},
+ {0, 0, 0, 0},
+ };
+
+ c = getopt_long(argc, argv, "hs:a:i:v:d:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage(argv[0]);
+ print_help();
+ exit(0);
+ break;
+ case 's':
+ socket_path = talloc_strdup(l23_ctx, optarg);
+ break;
+ case 'a':
+ ms->test_arfcn = atoi(optarg);
+ break;
+ case 'i':
+ if (!inet_aton(optarg, &gsmtap.sin_addr)) {
+ perror("inet_aton");
+ exit(2);
+ }
+ gsmtap_ip = ntohl(gsmtap.sin_addr.s_addr);
+ break;
+ case 'v':
+ vty_port = atoi(optarg);
+ break;
+ case 'd':
+ log_parse_category_mask(stderr_target, optarg);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void sighandler(int sigset)
+{
+ int rc = 0;
+
+ if (sigset == SIGHUP || sigset == SIGPIPE)
+ return;
+
+ fprintf(stderr, "Signal %d recevied.\n", sigset);
+ if (l23_app_exit)
+ rc = l23_app_exit(ms);
+
+ if (rc != -EBUSY)
+ exit (0);
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ printf("%s\n", openbsc_copyright);
+
+ INIT_LLIST_HEAD(&ms_list);
+ log_init(&log_info);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+
+ l23_ctx = talloc_named_const(NULL, 1, "layer2 context");
+
+ ms = talloc_zero(l23_ctx, struct osmocom_ms);
+ if (!ms) {
+ fprintf(stderr, "Failed to allocate MS\n");
+ exit(1);
+ }
+ llist_add_tail(&ms->entity, &ms_list);
+
+ sprintf(ms->name, "1");
+
+ ms->test_arfcn = 871;
+
+ handle_options(argc, argv);
+
+ rc = layer2_open(ms, socket_path);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during layer2_open()\n");
+ exit(1);
+ }
+
+ lapdm_init(&ms->l2_entity.lapdm_dcch, ms);
+ lapdm_init(&ms->l2_entity.lapdm_acch, ms);
+
+ rc = l23_app_init(ms);
+ if (rc < 0)
+ exit(1);
+
+ if (gsmtap_ip) {
+ rc = gsmtap_init(gsmtap_ip);
+ if (rc < 0) {
+ fprintf(stderr, "Failed during gsmtap_init()\n");
+ exit(1);
+ }
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGHUP, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGPIPE, sighandler);
+
+ while (!quit) {
+ if (l23_app_work)
+ l23_app_work(ms);
+ bsc_select_main(0);
+ }
+
+ return 0;
+}
diff --git a/src/host/layer23/src/mnccms.c b/src/host/layer23/src/mnccms.c
new file mode 100644
index 00000000..dd08b7c8
--- /dev/null
+++ b/src/host/layer23/src/mnccms.c
@@ -0,0 +1,458 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osmocore/talloc.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/mncc.h>
+#include <osmocom/vty.h>
+
+void *l23_ctx;
+static int new_callref = 1;
+static LLIST_HEAD(call_list);
+
+/*
+ * support functions
+ */
+
+void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
+
+static void free_call(struct gsm_call *call)
+{
+ llist_del(&call->entry);
+ DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
+ talloc_free(call);
+}
+
+
+struct gsm_call *get_call_ref(uint32_t callref)
+{
+ struct gsm_call *callt;
+
+ llist_for_each_entry(callt, &call_list, entry) {
+ if (callt->callref == callref)
+ return callt;
+ }
+ return NULL;
+}
+
+/*
+ * MNCCms dummy application
+ */
+
+/* this is a minimal implementation as required by GSM 04.08 */
+int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg)
+{
+ struct gsm_mncc *data = arg;
+ uint32_t callref = data->callref;
+ struct gsm_mncc rel;
+
+ if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF)
+ return 0;
+
+ LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call\n");
+
+ /* reject, as we don't support Calls */
+ memset(&rel, 0, sizeof(struct gsm_mncc));
+ rel.callref = callref;
+ mncc_set_cause(&rel, GSM48_CAUSE_LOC_USER,
+ GSM48_CC_CAUSE_INCOMPAT_DEST);
+
+ return mncc_send(ms, MNCC_REL_REQ, &rel);
+}
+
+/*
+ * MNCCms basic call application
+ */
+
+int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
+{
+ struct gsm_mncc *data = arg;
+ struct gsm_call *call = get_call_ref(data->callref);
+ struct gsm_mncc mncc;
+ uint8_t cause;
+
+ /* call does not exist */
+ if (!call && msg_type != MNCC_SETUP_IND) {
+ LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call "
+ "(callref %d)\n", data->callref);
+ if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF)
+ return 0;
+ cause = GSM48_CC_CAUSE_INCOMPAT_DEST;
+ release:
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = data->callref;
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_USER, cause);
+ return mncc_send(ms, MNCC_REL_REQ, &mncc);
+ }
+
+ /* setup without call */
+ if (!call) {
+ call = talloc_zero(l23_ctx, struct gsm_call);
+ if (!call)
+ return -ENOMEM;
+ call->callref = new_callref++;
+ llist_add_tail(&call->entry, &call_list);
+ }
+
+ switch (msg_type) {
+ case MNCC_DISC_IND:
+ vty_notify(ms, NULL);
+ switch (data->cause.value) {
+ case GSM48_CC_CAUSE_UNASSIGNED_NR:
+ vty_notify(ms, "Call: Number not assigned\n");
+ break;
+ case GSM48_CC_CAUSE_NO_ROUTE:
+ vty_notify(ms, "Call: Destination unreachable\n");
+ break;
+ case GSM48_CC_CAUSE_NORM_CALL_CLEAR:
+ vty_notify(ms, "Call: Remote hangs up\n");
+ break;
+ case GSM48_CC_CAUSE_USER_BUSY:
+ vty_notify(ms, "Call: Remote busy\n");
+ break;
+ case GSM48_CC_CAUSE_USER_NOTRESPOND:
+ vty_notify(ms, "Call: Remote not responding\n");
+ break;
+ case GSM48_CC_CAUSE_USER_ALERTING_NA:
+ vty_notify(ms, "Call: Remote not answering\n");
+ break;
+ case GSM48_CC_CAUSE_CALL_REJECTED:
+ vty_notify(ms, "Call has been rejected\n");
+ break;
+ case GSM48_CC_CAUSE_NUMBER_CHANGED:
+ vty_notify(ms, "Call: Number changed\n");
+ break;
+ case GSM48_CC_CAUSE_PRE_EMPTION:
+ vty_notify(ms, "Call: Cleared due to pre-emption\n");
+ break;
+ case GSM48_CC_CAUSE_DEST_OOO:
+ vty_notify(ms, "Call: Remote out of order\n");
+ break;
+ case GSM48_CC_CAUSE_INV_NR_FORMAT:
+ vty_notify(ms, "Call: Number invalid or imcomplete\n");
+ break;
+ case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN:
+ vty_notify(ms, "Call: No channel available\n");
+ break;
+ case GSM48_CC_CAUSE_NETWORK_OOO:
+ vty_notify(ms, "Call: Network out of order\n");
+ break;
+ case GSM48_CC_CAUSE_TEMP_FAILURE:
+ vty_notify(ms, "Call: Temporary failure\n");
+ break;
+ case GSM48_CC_CAUSE_SWITCH_CONG:
+ vty_notify(ms, "Congestion\n");
+ break;
+ default:
+ vty_notify(ms, "Call has been disconnected\n");
+ }
+ LOGP(DMNCC, LOGL_INFO, "Call has been disconnected "
+ "(cause %d)\n", data->cause.value);
+ if ((data->fields & MNCC_F_PROGRESS)
+ && data->progress.descr == 8) {
+ vty_notify(ms, "Please hang up!\n");
+ break;
+ }
+ free_call(call);
+ cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
+ goto release;
+ case MNCC_REL_IND:
+ case MNCC_REL_CNF:
+ vty_notify(ms, NULL);
+ if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED)
+ vty_notify(ms, "Call has been rejected\n");
+ else
+ vty_notify(ms, "Call has been released\n");
+ LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n",
+ data->cause.value);
+ free_call(call);
+ break;
+ case MNCC_CALL_PROC_IND:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call is proceeding\n");
+ LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n");
+ break;
+ case MNCC_ALERT_IND:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call is aleriting\n");
+ LOGP(DMNCC, LOGL_INFO, "Call is alerting\n");
+ break;
+ case MNCC_SETUP_CNF:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call is answered\n");
+ LOGP(DMNCC, LOGL_INFO, "Call is answered\n");
+ break;
+ case MNCC_SETUP_IND:
+ vty_notify(ms, NULL);
+ if (!llist_empty(&call_list) && !ms->settings.cw) {
+ vty_notify(ms, "Incomming call rejected while busy\n");
+ LOGP(DMNCC, LOGL_INFO, "Incomming call but busy\n");
+ cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
+ goto release;
+ }
+ if (!data->calling.present || !data->calling.number[0])
+ vty_notify(ms, "Incomming call\n");
+ else if (data->calling.type == 1)
+ vty_notify(ms, "Incomming call from +%s\n",
+ data->calling.number);
+ else if (data->calling.type == 2)
+ vty_notify(ms, "Incomming call from 0-%s\n",
+ data->calling.number);
+ else
+ vty_notify(ms, "Incomming call from %s\n",
+ data->calling.number);
+ LOGP(DMNCC, LOGL_INFO, "Incomming call\n");
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+ mncc_send(ms, MNCC_CALL_CONF_REQ, &mncc);
+ if (llist_empty(&call_list))
+ LOGP(DMNCC, LOGL_INFO, "Ring!\n");
+ else {
+ LOGP(DMNCC, LOGL_INFO, "Knock!\n");
+ call->hold = 1;
+ }
+ call->ring = 1;
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+ mncc_send(ms, MNCC_ALERT_REQ, &mncc);
+ break;
+ case MNCC_HOLD_CNF:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call is on hold\n");
+ LOGP(DMNCC, LOGL_INFO, "Call is on hold\n");
+ call->hold = 1;
+ break;
+ case MNCC_HOLD_REJ:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call hold was rejected\n");
+ LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n");
+ break;
+ case MNCC_RETRIEVE_CNF:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call is retrieved\n");
+ LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n");
+ call->hold = 0;
+ break;
+ case MNCC_RETRIEVE_REJ:
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Call retrieve was rejected\n");
+ LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n");
+ break;
+ default:
+ LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n",
+ msg_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mncc_call(struct osmocom_ms *ms, char *number)
+{
+ struct gsm_call *call;
+ struct gsm_mncc setup;
+
+ llist_for_each_entry(call, &call_list, entry) {
+ if (!call->hold) {
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Please put active call on hold "
+ "first!\n");
+ LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n");
+ return -EBUSY;
+ }
+ }
+
+ call = talloc_zero(l23_ctx, struct gsm_call);
+ if (!call)
+ return -ENOMEM;
+ call->callref = new_callref++;
+ llist_add_tail(&call->entry, &call_list);
+
+ memset(&setup, 0, sizeof(struct gsm_mncc));
+ setup.callref = call->callref;
+
+ if (!strncasecmp(number, "emerg", 5)) {
+ LOGP(DMNCC, LOGL_INFO, "Make emergency call\n");
+ /* emergency */
+ setup.emergency = 1;
+ } else {
+ LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number);
+ /* called number */
+ setup.fields |= MNCC_F_CALLED;
+ strncpy(setup.called.number, number,
+ sizeof(setup.called.number) - 1);
+
+ /* bearer capability (mandatory) */
+ setup.fields |= MNCC_F_BEARER_CAP;
+ setup.bearer_cap.coding = 0;
+ setup.bearer_cap.radio = 1;
+ setup.bearer_cap.speech_ctm = 0;
+ setup.bearer_cap.speech_ver[0] = 0;
+ setup.bearer_cap.speech_ver[1] = -1; /* end of list */
+ setup.bearer_cap.transfer = 0;
+ setup.bearer_cap.mode = 0;
+ if (ms->settings.clir)
+ setup.clir.sup = 1;
+ else if (ms->settings.clip)
+ setup.clir.inv = 1;
+ }
+
+ return mncc_send(ms, MNCC_SETUP_REQ, &setup);
+}
+
+int mncc_hangup(struct osmocom_ms *ms)
+{
+ struct gsm_call *call, *found = NULL;
+ struct gsm_mncc disc;
+
+ llist_for_each_entry(call, &call_list, entry) {
+ if (!call->hold) {
+ found = call;
+ break;
+ }
+ }
+ if (!found) {
+ LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n");
+ vty_notify(ms, NULL);
+ vty_notify(ms, "No active call\n");
+ return -EINVAL;
+ }
+
+ memset(&disc, 0, sizeof(struct gsm_mncc));
+ disc.callref = found->callref;
+ mncc_set_cause(&disc, GSM48_CAUSE_LOC_USER,
+ GSM48_CC_CAUSE_NORM_CALL_CLEAR);
+ return mncc_send(ms, MNCC_DISC_REQ, &disc);
+}
+
+int mncc_answer(struct osmocom_ms *ms)
+{
+ struct gsm_call *call, *alerting = NULL;
+ struct gsm_mncc rsp;
+ int active = 0;
+
+ llist_for_each_entry(call, &call_list, entry) {
+ if (call->ring)
+ alerting = call;
+ if (!call->hold)
+ active = 1;
+ }
+ if (!alerting) {
+ LOGP(DMNCC, LOGL_INFO, "No call alerting\n");
+ vty_notify(ms, NULL);
+ vty_notify(ms, "No alerting call\n");
+ return -EBUSY;
+ }
+ if (active) {
+ LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n");
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Please put active call on hold first!\n");
+ return -EBUSY;
+ }
+ alerting->ring = 0;
+
+ memset(&rsp, 0, sizeof(struct gsm_mncc));
+ rsp.callref = alerting->callref;
+ return mncc_send(ms, MNCC_SETUP_RSP, &rsp);
+}
+
+int mncc_hold(struct osmocom_ms *ms)
+{
+ struct gsm_call *call, *found = NULL;
+ struct gsm_mncc hold;
+
+ llist_for_each_entry(call, &call_list, entry) {
+ if (!call->hold) {
+ found = call;
+ break;
+ }
+ }
+ if (!found) {
+ LOGP(DMNCC, LOGL_INFO, "No active call to hold\n");
+ vty_notify(ms, NULL);
+ vty_notify(ms, "No active call\n");
+ return -EINVAL;
+ }
+
+ memset(&hold, 0, sizeof(struct gsm_mncc));
+ hold.callref = found->callref;
+ return mncc_send(ms, MNCC_HOLD_REQ, &hold);
+}
+
+int mncc_retrieve(struct osmocom_ms *ms, int number)
+{
+ struct gsm_call *call;
+ struct gsm_mncc retr;
+ int holdnum = 0, active = 0, i = 0;
+
+ llist_for_each_entry(call, &call_list, entry) {
+ if (call->hold)
+ holdnum++;
+ if (!call->hold)
+ active = 1;
+ }
+ if (active) {
+ LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n");
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Hold active call first!\n");
+ return -EINVAL;
+ }
+ if (holdnum == 0) {
+ vty_notify(ms, NULL);
+ vty_notify(ms, "No call on hold!\n");
+ return -EINVAL;
+ }
+ if (holdnum > 1 && number <= 0) {
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Select call 1..%d\n", holdnum);
+ return -EINVAL;
+ }
+ if (holdnum == 1 && number <= 0)
+ number = 1;
+ if (number > holdnum) {
+ vty_notify(ms, NULL);
+ vty_notify(ms, "Given number %d out of range!\n", number);
+ vty_notify(ms, "Select call 1..%d\n", holdnum);
+ return -EINVAL;
+ }
+
+ llist_for_each_entry(call, &call_list, entry) {
+ i++;
+ if (i == number)
+ break;
+ }
+
+ memset(&retr, 0, sizeof(struct gsm_mncc));
+ retr.callref = call->callref;
+ return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr);
+}
+
+
+
+
diff --git a/src/host/layer23/src/networks.c b/src/host/layer23/src/networks.c
new file mode 100644
index 00000000..ae14d80c
--- /dev/null
+++ b/src/host/layer23/src/networks.c
@@ -0,0 +1,1824 @@
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <osmocom/networks.h>
+
+/* list of networks */
+
+struct gsm_networks gsm_networks[] = {
+ { 0x001, -1, "Test" },
+ { 0x001, 0x01f, "Test" },
+ { 0x412, -1, "Afghanistan" },
+ { 0x412, 0x01f, "AWCC" },
+ { 0x412, 0x20f, "Roshan" },
+ { 0x412, 0x40f, "Areeba" },
+ { 0x412, 0x50f, "Etisalat" },
+ { 0x276, -1, "Albania" },
+ { 0x276, 0x01f, "AMC" },
+ { 0x276, 0x02f, "Vodafone" },
+ { 0x276, 0x03f, "Eagle Mobile" },
+ { 0x603, -1, "Algeria" },
+ { 0x603, 0x01f, "Mobilis" },
+ { 0x603, 0x02f, "Djezzy" },
+ { 0x603, 0x03f, "Nedjma" },
+ { 0x213, -1, "Andorra" },
+ { 0x213, 0x03f, "Mobiland" },
+ { 0x631, -1, "Angola" },
+ { 0x631, 0x02f, "UNITEL" },
+ { 0x365, -1, "Anguilla" },
+ { 0x365, 0x010, "Weblinks Limited" },
+ { 0x365, 0x840, "Cable & Wireless" },
+ { 0x344, -1, "Antigua and Barbuda" },
+ { 0x344, 0x030, "APUA" },
+ { 0x344, 0x920, "Lime" },
+ { 0x338, 0x050, "Digicel" },
+ { 0x722, -1, "Argentina" },
+ { 0x722, 0x010, "Movistar" },
+ { 0x722, 0x020, "Nextel" },
+ { 0x722, 0x070, "Movistar" },
+ { 0x722, 0x310, "Claro" },
+ { 0x722, 0x320, "Claro" },
+ { 0x722, 0x330, "Claro" },
+ { 0x722, 0x340, "Personal" },
+ { 0x722, 0x350, "Hutchinson (PORT HABLE)" },
+ { 0x283, -1, "Armenia" },
+ { 0x283, 0x01f, "Beeline" },
+ { 0x283, 0x05f, "VivaCell-MTS" },
+ { 0x283, 0x10f, "Orange" },
+ { 0x363, -1, "Aruba" },
+ { 0x363, 0x01f, "SETAR" },
+ { 0x363, 0x02f, "Digicel" },
+ { 0x505, -1, "Australia" },
+ { 0x505, 0x01f, "Telstra" },
+ { 0x505, 0x02f, "Optus" },
+ { 0x505, 0x03f, "Vodafone" },
+ { 0x505, 0x04f, "Department of Defence" },
+ { 0x505, 0x05f, "Ozitel" },
+ { 0x505, 0x06f, "Vodafone" },
+ { 0x505, 0x08f, "One.Tel" },
+ { 0x505, 0x09f, "Airnet" },
+ { 0x505, 0x12f, "3" },
+ { 0x505, 0x13f, "Railcorp" },
+ { 0x505, 0x14f, "AAPT" },
+ { 0x505, 0x15f, "3GIS" },
+ { 0x505, 0x16f, "Unknown" },
+ { 0x505, 0x21f, "SOUL" },
+ { 0x505, 0x24f, "Advanced Communications Technologies Pty. Ltd." },
+ { 0x505, 0x38f, "Crazy John's" },
+ { 0x505, 0x71f, "Telstra" },
+ { 0x505, 0x72f, "Telstra" },
+ { 0x505, 0x88f, "Localstar Holding Pty. Ltd." },
+ { 0x505, 0x90f, "Optus" },
+ { 0x505, 0x99f, "One.Tel" },
+ { 0x232, -1, "Austria" },
+ { 0x232, 0x01f, "A1" },
+ { 0x232, 0x03f, "T-Mobile" },
+ { 0x232, 0x05f, "Orange" },
+ { 0x232, 0x07f, "tele.ring" },
+ { 0x232, 0x10f, "3" },
+ { 0x232, 0x11f, "bob" },
+ { 0x232, 0x12f, "yesss" },
+ { 0x232, 0x14f, "3" },
+ { 0x232, 0x15f, "Barablu" },
+ { 0x232, 0x16f, "3" },
+ { 0x400, -1, "Azerbaijan" },
+ { 0x400, 0x01f, "Azercell" },
+ { 0x400, 0x02f, "Bakcell" },
+ { 0x400, 0x03f, "FONEX" },
+ { 0x400, 0x04f, "Nar Mobile" },
+ { 0x364, -1, "Bahamas" },
+ { 0x364, 0x390, "BaTelCo" },
+ { 0x426, -1, "Bahrain" },
+ { 0x426, 0x01f, "Batelco" },
+ { 0x426, 0x02f, "zain BH" },
+ { 0x426, 0x04f, "VIVA" },
+ { 0x470, -1, "Bangladesh" },
+ { 0x470, 0x01f, "Grameenphone" },
+ { 0x470, 0x02f, "Robi" },
+ { 0x470, 0x03f, "Banglalink" },
+ { 0x470, 0x04f, "TeleTalk" },
+ { 0x470, 0x05f, "Citycell" },
+ { 0x470, 0x06f, "Warid" },
+ { 0x470, 0x07f, "WTBL" },
+ { 0x342, -1, "Barbados" },
+ { 0x342, 0x600, "bmobile" },
+ { 0x342, 0x750, "Digicel" },
+ { 0x342, 0x820, "Sunbeach Communications" },
+ { 0x257, -1, "Belarus" },
+ { 0x257, 0x01f, "velcom" },
+ { 0x257, 0x02f, "MTS" },
+ { 0x257, 0x04f, "life:)" },
+ { 0x257, 0x03f, "DIALLOG" },
+ { 0x206, -1, "Belgium" },
+ { 0x206, 0x01f, "Proximus" },
+ { 0x206, 0x02f, "SNCB GSM-R" },
+ { 0x206, 0x10f, "Mobistar" },
+ { 0x206, 0x20f, "BASE" },
+ { 0x702, -1, "Belize" },
+ { 0x702, 0x67f, "Belize Telemedia" },
+ { 0x702, 0x68f, "International Telecommunications Ltd." },
+ { 0x702, 0x00f, "Smart" },
+ { 0x616, -1, "Benin" },
+ { 0x616, 0x01f, "Libercom" },
+ { 0x616, 0x02f, "Moov" },
+ { 0x616, 0x03f, "MTN" },
+ { 0x616, 0x04f, "BBCOM" },
+ { 0x616, 0x05f, "Glo" },
+ { 0x350, -1, "Bermuda" },
+ { 0x350, 0x01f, "Digicel Bermuda" },
+ { 0x350, 0x02f, "Mobility" },
+ { 0x338, 0x050, "Digicel Bermuda" },
+ { 0x310, 0x00f, "Cellular One" },
+ { 0x402, -1, "Bhutan" },
+ { 0x402, 0x11f, "B-Mobile" },
+ { 0x402, 0x77f, "TashiCell" },
+ { 0x736, -1, "Bolivia" },
+ { 0x736, 0x01f, "Nuevatel" },
+ { 0x736, 0x02f, "Entel" },
+ { 0x736, 0x03f, "Tigo" },
+ { 0x218, -1, "Bosnia and Herzegovina" },
+ { 0x218, 0x03f, "HT-ERONET" },
+ { 0x218, 0x05f, "m:tel" },
+ { 0x218, 0x90f, "BH Mobile" },
+ { 0x652, -1, "Botswana" },
+ { 0x652, 0x01f, "Mascom" },
+ { 0x652, 0x02f, "Orange" },
+ { 0x652, 0x04f, "BTC Mobile" },
+ { 0x724, -1, "Brazil" },
+ { 0x724, 0x02f, "TIM" },
+ { 0x724, 0x03f, "TIM" },
+ { 0x724, 0x04f, "TIM" },
+ { 0x724, 0x05f, "Claro" },
+ { 0x724, 0x06f, "Vivo" },
+ { 0x724, 0x07f, "CTBC Celular" },
+ { 0x724, 0x08f, "TIM" },
+ { 0x724, 0x10f, "Vivo" },
+ { 0x724, 0x11f, "Vivo" },
+ { 0x724, 0x15f, "Sercomtel" },
+ { 0x724, 0x16f, "Oi / Brasil Telecom" },
+ { 0x724, 0x23f, "Vivo" },
+ { 0x724, 0x24f, "Oi / Amazonia Celular" },
+ { 0x724, 0x31f, "Oi" },
+ { 0x724, 0x32f, "CTBC Celular" },
+ { 0x724, 0x33f, "CTBC Celular" },
+ { 0x724, 0x34f, "CTBC Celular" },
+ { 0x724, 0x37f, "aeiou" },
+ { 0x724, 0x00f, "Nextel" },
+ { 0x348, -1, "British Virgin Islands" },
+ { 0x348, 0x170, "Cable & Wireless" },
+ { 0x348, 0x570, "CCT Boatphone" },
+ { 0x528, -1, "Brunei" },
+ { 0x528, 0x01f, "Jabatan Telekom" },
+ { 0x528, 0x02f, "B-Mobile" },
+ { 0x528, 0x11f, "DSTCom" },
+ { 0x284, -1, "Bulgaria" },
+ { 0x284, 0x01f, "M-Tel" },
+ { 0x284, 0x03f, "Vivacom" },
+ { 0x284, 0x05f, "GLOBUL" },
+ { 0x613, -1, "Burkina Faso" },
+ { 0x613, 0x01f, "Onatel" },
+ { 0x613, 0x02f, "Zain" },
+ { 0x613, 0x03f, "Telecel Faso" },
+ { 0x642, -1, "Burundi" },
+ { 0x642, 0x01f, "Spacetel" },
+ { 0x642, 0x02f, "Africell" },
+ { 0x642, 0x03f, "Telecel" },
+ { 0x642, 0x82f, "Onatel" },
+// { 0x642, ?, "LACELL SU" },
+ { 0x456, -1, "Cambodia" },
+ { 0x456, 0x01f, "Mobitel" },
+ { 0x456, 0x02f, "hello" },
+ { 0x456, 0x03f, "S Telecom" },
+ { 0x456, 0x04f, "qb" },
+ { 0x456, 0x05f, "Star-Cell" },
+ { 0x456, 0x06f, "Smart" },
+ { 0x456, 0x18f, "Mfone" },
+// { 0x456, ?, "Excell" },
+ { 0x456, 0x09f, "Beeline" },
+ { 0x456, 0x08f, "Metfone" },
+ { 0x624, -1, "Cameroon" },
+ { 0x624, 0x01f, "MTN Cameroon" },
+ { 0x624, 0x02f, "Orange" },
+ { 0x302, -1, "Canada" },
+ { 0x302, 0x220, "Telus" },
+ { 0x302, 0x221, "Telus" },
+ { 0x302, 0x290, "Airtel Wireless" },
+ { 0x302, 0x350, "FIRST" },
+ { 0x302, 0x360, "MiKe" },
+ { 0x302, 0x361, "Telus" },
+ { 0x302, 0x370, "Fido" },
+ { 0x302, 0x380, "DMTS" },
+ { 0x302, 0x490, "WIND Mobile" },
+ { 0x302, 0x500, "Videotron" },
+ { 0x302, 0x510, "Videotron" },
+ { 0x302, 0x610, "Bell" },
+ { 0x302, 0x620, "ICE Wireless" },
+ { 0x302, 0x640, "Bell" },
+ { 0x302, 0x651, "Bell" },
+ { 0x302, 0x652, "BC Tel Mobility (Telus)" },
+ { 0x302, 0x653, "Telus" },
+ { 0x302, 0x655, "MTS" },
+ { 0x302, 0x656, "TBay" },
+ { 0x302, 0x657, "Telus" },
+ { 0x302, 0x680, "SaskTel" },
+ { 0x302, 0x701, "MB Tel Mobility" },
+ { 0x302, 0x702, "MT&T Mobility (Aliant)" },
+ { 0x302, 0x703, "New Tel Mobility (Aliant)" },
+ { 0x302, 0x710, "Globalstar" },
+ { 0x302, 0x720, "Rogers Wireless" },
+ { 0x302, 0x780, "SaskTel" },
+ { 0x302, 0x880, "Bell / Telus" },
+ { 0x625, -1, "Cape Verde" },
+ { 0x625, 0x01f, "CVMOVEL" },
+ { 0x625, 0x02f, "T+" },
+ { 0x346, -1, "Cayman Islands" },
+ { 0x346, 0x140, "Cable & Wireless" },
+ { 0x338, 0x050, "Digicel" },
+ { 0x623, -1, "Central African Republic" },
+ { 0x623, 0x01f, "CTP" },
+ { 0x623, 0x02f, "TC" },
+ { 0x623, 0x03f, "Orange" },
+ { 0x623, 0x04f, "Nationlink" },
+ { 0x622, -1, "Chad" },
+ { 0x622, 0x01f, "Zain" },
+ { 0x622, 0x02f, "Tchad Mobile" },
+ { 0x622, 0x03f, "TIGO - Millicom" },
+ { 0x622, 0x02f, "TAWALI" },
+ { 0x622, 0x04f, "Salam" },
+ { 0x730, -1, "Chile" },
+ { 0x730, 0x01f, "Entel" },
+ { 0x730, 0x02f, "movistar" },
+ { 0x730, 0x03f, "Claro" },
+ { 0x730, 0x04f, "Nextel" },
+ { 0x730, 0x10f, "Entel" },
+ { 0x730, 0x99f, "WILL" },
+ { 0x460, -1, "China" },
+ { 0x460, 0x00f, "China Mobile" },
+ { 0x460, 0x01f, "China Unicom" },
+ { 0x460, 0x02f, "China Mobile" },
+ { 0x460, 0x03f, "China Telecom" },
+ { 0x460, 0x05f, "China Telecom" },
+ { 0x460, 0x06f, "China Unicom" },
+ { 0x460, 0x20f, "China TIETONG" },
+ { 0x732, -1, "Colombia" },
+ { 0x732, 0x001, "Colombia Telecomunicaciones S.A." },
+ { 0x732, 0x002, "Edatel" },
+ { 0x732, 0x101, "Comcel" },
+ { 0x732, 0x102, "movistar" },
+ { 0x732, 0x103, "Tigo" },
+ { 0x732, 0x111, "Tigo" },
+ { 0x732, 0x12f, "movistar" },
+ { 0x654, -1, "Comoros" },
+ { 0x654, 0x01f, "HURI - SNPT" },
+ { 0x629, -1, "Republic of the Congo" },
+ { 0x629, 0x01f, "Zain" },
+ { 0x629, 0x10f, "Libertis Telecom" },
+// { 0x629, ?, "Warid Telecom" },
+ { 0x548, -1, "Cook Islands" },
+ { 0x548, 0x01f, "Telecom Cook" },
+ { 0x712, -1, "Costa Rica" },
+ { 0x712, 0x01f, "ICE" },
+ { 0x712, 0x02f, "ICE" },
+ { 0x712, 0x03f, "ICE" },
+ { 0x219, -1, "Croatia" },
+ { 0x219, 0x01f, "T-Mobile" },
+ { 0x219, 0x02f, "Tele2" },
+ { 0x219, 0x10f, "VIPnet" },
+ { 0x368, -1, "Cuba" },
+ { 0x368, 0x01f, "ETECSA" },
+ { 0x280, -1, "Cyprus" },
+ { 0x280, 0x01f, "Cytamobile-Vodafone" },
+ { 0x280, 0x10f, "MTN" },
+ { 0x230, -1, "Czech Republic" },
+ { 0x230, 0x01f, "T-Mobile" },
+ { 0x230, 0x02f, "O2" },
+ { 0x230, 0x03f, "Vodafone" },
+ { 0x230, 0x04f, "U:fon" },
+ { 0x230, 0x98f, "S?DC s.o." },
+ { 0x230, 0x99f, "Vodafone" },
+ { 0x630, -1, "Democratic Republic of the Congo" },
+ { 0x630, 0x01f, "Vodacom" },
+ { 0x630, 0x02f, "Zain" },
+ { 0x630, 0x04f, "Cellco" },
+ { 0x630, 0x05f, "Supercell" },
+ { 0x630, 0x86f, "CCT" },
+ { 0x630, 0x89f, "SAIT Telecom" },
+// { 0x630, ?, "Africell" },
+ { 0x238, -1, "Denmark" },
+ { 0x238, 0x01f, "TDC" },
+ { 0x238, 0x02f, "Telenor" },
+ { 0x238, 0x03f, "MIGway A/S" },
+ { 0x238, 0x05f, "ApS KBUS" },
+ { 0x238, 0x06f, "3" },
+ { 0x238, 0x07f, "Barablu Mobile Ltd." },
+ { 0x238, 0x09f, "Dansk Beredskabskommunikation A/S" },
+ { 0x238, 0x10f, "TDC" },
+ { 0x238, 0x11f, "Dansk Beredskabskommunikation A/S" },
+ { 0x238, 0x12f, "Lycamobile Denmark Ltd" },
+ { 0x238, 0x20f, "Telia" },
+ { 0x238, 0x30f, "Telia" },
+ { 0x238, 0x40f, "Ericsson Danmark A/S" },
+ { 0x238, 0x77f, "Telenor" },
+ { 0x638, -1, "Djibouti" },
+ { 0x638, 0x01f, "Evatis" },
+ { 0x366, -1, "Dominica" },
+ { 0x366, 0x020, "Digicel" },
+ { 0x366, 0x110, "Cable & Wireless" },
+ { 0x370, -1, "Dominican Republic" },
+ { 0x370, 0x01f, "Orange" },
+ { 0x370, 0x02f, "Claro" },
+ { 0x370, 0x03f, "Tricom" },
+ { 0x370, 0x04f, "Viva" },
+ { 0x514, -1, "East Timor" },
+ { 0x514, 0x02f, "Timor Telecom" },
+ { 0x740, -1, "Ecuador" },
+ { 0x740, 0x00f, "Movistar" },
+ { 0x740, 0x01f, "Porta" },
+ { 0x740, 0x02f, "Alegro" },
+ { 0x602, -1, "Egypt" },
+ { 0x602, 0x01f, "Mobinil" },
+ { 0x602, 0x02f, "Vodafone" },
+ { 0x602, 0x03f, "Etisalat" },
+ { 0x706, -1, "El Salvador" },
+ { 0x706, 0x01f, "CTE Telecom Personal" },
+ { 0x706, 0x02f, "digicel" },
+ { 0x706, 0x03f, "Telemovil EL Salvador" },
+ { 0x706, 0x04f, "movistar" },
+ { 0x706, 0x10f, "Claro" },
+ { 0x627, -1, "Equatorial Guinea" },
+ { 0x627, 0x01f, "Orange GQ" },
+ { 0x627, 0x03f, "Hits GQ" },
+ { 0x657, -1, "Eritrea" },
+ { 0x657, 0x01f, "Eritel" },
+ { 0x248, -1, "Estonia" },
+ { 0x248, 0x01f, "EMT" },
+ { 0x248, 0x02f, "Elisa" },
+ { 0x248, 0x03f, "Tele 2" },
+ { 0x248, 0x04f, "OY Top Connect" },
+ { 0x248, 0x05f, "AS Bravocom Mobiil" },
+ { 0x248, 0x06f, "OY ViaTel" },
+ { 0x636, -1, "Ethiopia" },
+ { 0x636, 0x01f, "ETMTN" },
+ { 0x288, -1, "Faroe Islands" },
+ { 0x288, 0x01f, "Faroese Telecom" },
+ { 0x288, 0x02f, "Vodafone" },
+ { 0x542, -1, "Fiji" },
+ { 0x542, 0x01f, "Vodafone" },
+ { 0x542, 0x02f, "Digicel" },
+ { 0x244, -1, "Finland" },
+ { 0x244, 0x03f, "DNA" },
+ { 0x244, 0x05f, "Elisa" },
+ { 0x244, 0x07f, "Nokia" },
+ { 0x244, 0x08f, "Unknown" },
+ { 0x244, 0x10f, "TDC Oy" },
+ { 0x244, 0x12f, "DNA" },
+ { 0x244, 0x14f, "AMT" },
+ { 0x244, 0x21f, "Saunalahti" },
+ { 0x244, 0x29f, "Scnl Truphone" },
+ { 0x244, 0x91f, "Sonera" },
+ { 0x208, -1, "France" },
+ { 0x208, 0x00f, "Orange" },
+ { 0x208, 0x01f, "Orange" },
+ { 0x208, 0x02f, "Orange" },
+ { 0x208, 0x05f, "Globalstar Europe" },
+ { 0x208, 0x06f, "Globalstar Europe" },
+ { 0x208, 0x07f, "Globalstar Europe" },
+ { 0x208, 0x10f, "SFR" },
+ { 0x208, 0x11f, "SFR" },
+ { 0x208, 0x13f, "SFR" },
+ { 0x208, 0x20f, "Bouygues" },
+ { 0x208, 0x21f, "Bouygues" },
+ { 0x208, 0x22f, "Transatel Mobile" },
+ { 0x208, 0x88f, "Bouygues" },
+ { 0x628, -1, "Gabon" },
+ { 0x628, 0x01f, "Libertis" },
+ { 0x628, 0x02f, "Moov (Telecel) Gabon S.A." },
+ { 0x628, 0x03f, "Zain" },
+ { 0x607, -1, "Gambia" },
+ { 0x607, 0x01f, "Gamcel" },
+ { 0x607, 0x02f, "Africel" },
+ { 0x607, 0x03f, "Comium" },
+ { 0x607, 0x04f, "QCell" },
+ { 0x282, -1, "Georgia" },
+ { 0x282, 0x01f, "Geocell" },
+ { 0x282, 0x02f, "MagtiCom" },
+ { 0x282, 0x03f, "Iberiatel" },
+ { 0x282, 0x04f, "Beeline" },
+ { 0x289, 0x67f, "Aquafon" },
+ { 0x289, 0x88f, "A-Mobile" },
+ { 0x262, -1, "Germany" },
+ { 0x262, 0x01f, "T-Mobile" },
+ { 0x262, 0x02f, "Vodafone" },
+ { 0x262, 0x03f, "E-Plus" },
+ { 0x262, 0x04f, "Vodafone" },
+ { 0x262, 0x05f, "E-Plus" },
+ { 0x262, 0x06f, "T-Mobile" },
+ { 0x262, 0x07f, "O2" },
+ { 0x262, 0x08f, "O2" },
+ { 0x262, 0x09f, "Vodafone" },
+ { 0x262, 0x10f, "DB Systel GSM-R" },
+ { 0x262, 0x11f, "O2" },
+ { 0x262, 0x12f, "Dolphin Telecom" },
+ { 0x262, 0x13f, "Mobilcom Multimedia" },
+ { 0x262, 0x14f, "Group 3G UMTS" },
+ { 0x262, 0x15f, "Airdata" },
+ { 0x262, 0x16f, "Vistream" },
+ { 0x262, 0x42f, "OpenBSC" },
+ { 0x262, 0x60f, "DB Telematik" },
+ { 0x262, 0x76f, "Siemens AG" },
+ { 0x262, 0x77f, "E-Plus" },
+ { 0x262, 0x901, "Debitel" },
+ { 0x620, -1, "Ghana" },
+ { 0x620, 0x01f, "MTN" },
+ { 0x620, 0x02f, "Vodafone" },
+ { 0x620, 0x03f, "tiGO" },
+ { 0x620, 0x04f, "Kasapa / Hutchison Telecom" },
+ { 0x620, 0x06f, "Zain" },
+ { 0x266, -1, "Gibraltar" },
+ { 0x266, 0x01f, "GibTel" },
+ { 0x266, 0x06f, "CTS Mobile" },
+ { 0x202, -1, "Greece" },
+ { 0x202, 0x01f, "Cosmote" },
+ { 0x202, 0x05f, "Vodafone" },
+ { 0x202, 0x09f, "Wind" },
+ { 0x202, 0x10f, "Wind" },
+ { 0x290, -1, "Greenland" },
+ { 0x290, 0x01f, "TELE Greenland A/S" },
+ { 0x352, -1, "Grenada" },
+ { 0x352, 0x030, "Digicel" },
+ { 0x352, 0x110, "Cable & Wireless" },
+ { 0x340, -1, "Guadeloupe" },
+ { 0x340, 0x01f, "Orange" },
+ { 0x340, 0x02f, "Outremer" },
+ { 0x340, 0x03f, "Telcell" },
+ { 0x340, 0x08f, "MIO GSM" },
+ { 0x340, 0x20f, "Digicel" },
+ { 0x310, -1, "Guam" },
+ { 0x310, 0x032, "IT&E Wireless" },
+ { 0x310, 0x033, "Guam Telephone Authority" },
+ { 0x310, 0x140, "mPulse" },
+ { 0x310, 0x370, "Guamcell" },
+ { 0x311, 0x250, "i CAN_GSM" },
+ { 0x310, 0x470, "Guamcell" },
+ { 0x704, -1, "Guatemala" },
+ { 0x704, 0x01f, "Claro" },
+ { 0x704, 0x02f, "Comcel / Tigo" },
+ { 0x704, 0x03f, "movistar" },
+// { 0x704, ?, "digicel" },
+ { 0x234, -1, "Guernsey" },
+ { 0x234, 0x55f, "Sure Mobile" },
+ { 0x234, 0x50f, "Wave Telecom" },
+ { 0x234, 0x03f, "Airtel Vodafone" },
+ { 0x611, -1, "Guinea" },
+ { 0x611, 0x01f, "Spacetel" },
+ { 0x611, 0x02f, "Lagui" },
+ { 0x611, 0x03f, "Telecel Guinee" },
+ { 0x611, 0x04f, "MTN" },
+ { 0x632, -1, "Guinea-Bissau" },
+ { 0x632, 0x02f, "Areeba" },
+ { 0x632, 0x03f, "Orange" },
+ { 0x738, -1, "Guyana" },
+ { 0x738, 0x01f, "Digicel" },
+ { 0x738, 0x02f, "GT&T Cellink Plus" },
+ { 0x372, -1, "Haiti" },
+ { 0x372, 0x01f, "Comcel / Voila" },
+ { 0x338, 0x050, "Digicel" },
+ { 0x708, -1, "Honduras" },
+ { 0x708, 0x01f, "Claro" },
+ { 0x708, 0x02f, "Celtel / Tigo" },
+ { 0x708, 0x30f, "Hondutel" },
+ { 0x708, 0x40f, "DIGICEL" },
+ { 0x454, -1, "Hong Kong" },
+ { 0x454, 0x00f, "1O1O and One2Free" },
+ { 0x454, 0x01f, "CITIC Telecom 1616" },
+ { 0x454, 0x02f, "CSL Limited" },
+ { 0x454, 0x03f, "3 (3G)" },
+ { 0x454, 0x04f, "3 DualBand (2G)" },
+ { 0x454, 0x05f, "3 CDMA" },
+ { 0x454, 0x06f, "SmarTone-Vodafone" },
+ { 0x454, 0x07f, "China Unicom (Hong Kong) Limited" },
+ { 0x454, 0x08f, "Trident" },
+ { 0x454, 0x09f, "China Motion Telecom" },
+ { 0x454, 0x10f, "New World Mobility" },
+ { 0x454, 0x11f, "China-Hongkong Telecom" },
+ { 0x454, 0x12f, "CMCC HK" },
+ { 0x454, 0x14f, "Hutchison Telecom" },
+ { 0x454, 0x15f, "SmarTone Mobile Communications Limited" },
+ { 0x454, 0x16f, "PCCW Mobile (2G)" },
+ { 0x454, 0x17f, "SmarTone Mobile Communications Limited" },
+ { 0x454, 0x18f, "CSL Limited" },
+ { 0x454, 0x19f, "PCCW Mobile (3G)" },
+ { 0x454, 0x29f, "PCCW Mobile (CDMA)" },
+ { 0x274, -1, "Iceland" },
+ { 0x274, 0x01f, "Siminn" },
+ { 0x274, 0x02f, "Vodafone" },
+ { 0x274, 0x03f, "Vodafone" },
+ { 0x274, 0x04f, "Viking" },
+ { 0x274, 0x06f, "N?ll n?u ehf" },
+ { 0x274, 0x07f, "IceCell" },
+ { 0x274, 0x08f, "On-waves" },
+ { 0x274, 0x11f, "Nova" },
+ { 0x404, -1, "India" },
+ { 0x404, 0x01f, "Vodafone IN" },
+ { 0x404, 0x02f, "AirTel" },
+ { 0x404, 0x04f, "IDEA" },
+ { 0x404, 0x05f, "Vodafone IN" },
+ { 0x404, 0x07f, "IDEA" },
+ { 0x404, 0x09f, "Reliance" },
+ { 0x404, 0x10f, "AirTel" },
+ { 0x404, 0x11f, "Vodafone IN" },
+ { 0x404, 0x12f, "IDEA" },
+ { 0x404, 0x13f, "Vodafone IN" },
+ { 0x404, 0x14f, "IDEA" },
+ { 0x404, 0x15f, "Vodafone IN" },
+ { 0x404, 0x17f, "AIRCEL" },
+ { 0x404, 0x19f, "IDEA" },
+ { 0x404, 0x20f, "Vodafone IN" },
+ { 0x404, 0x21f, "Loop Mobile" },
+ { 0x404, 0x22f, "IDEA" },
+ { 0x404, 0x24f, "IDEA" },
+ { 0x404, 0x27f, "Vodafone IN" },
+ { 0x404, 0x28f, "AIRCEL" },
+ { 0x404, 0x29f, "AIRCEL" },
+ { 0x404, 0x30f, "Vodafone IN" },
+ { 0x404, 0x31f, "AirTel" },
+ { 0x404, 0x34f, "CellOne" },
+ { 0x404, 0x36f, "Reliance" },
+ { 0x404, 0x37f, "Aircel" },
+ { 0x404, 0x38f, "CellOne" },
+ { 0x404, 0x41f, "Aircel" },
+ { 0x404, 0x42f, "Aircel" },
+ { 0x404, 0x44f, "IDEA" },
+ { 0x404, 0x45f, "Airtel" },
+ { 0x404, 0x51f, "CellOne" },
+ { 0x404, 0x52f, "Reliance" },
+ { 0x404, 0x53f, "CellOne" },
+ { 0x404, 0x54f, "CellOne" },
+ { 0x404, 0x55f, "CellOne" },
+ { 0x404, 0x56f, "IDEA" },
+ { 0x404, 0x57f, "CellOne" },
+ { 0x404, 0x58f, "CellOne" },
+ { 0x404, 0x59f, "CellOne" },
+ { 0x404, 0x60f, "Vodafone IN" },
+ { 0x404, 0x62f, "CellOne" },
+ { 0x404, 0x64f, "CellOne" },
+ { 0x404, 0x66f, "CellOne" },
+ { 0x404, 0x67f, "Reliance GSM" },
+ { 0x404, 0x68f, "DOLPHIN" },
+ { 0x404, 0x69f, "DOLPHIN" },
+ { 0x404, 0x72f, "CellOne" },
+ { 0x404, 0x74f, "CellOne" },
+ { 0x404, 0x76f, "CellOne" },
+ { 0x404, 0x78f, "Idea Cellular Ltd" },
+ { 0x404, 0x80f, "BSNL MOBILE" },
+ { 0x404, 0x81f, "CellOne" },
+ { 0x404, 0x82f, "Idea" },
+ { 0x404, 0x83f, "Reliance Smart GSM" },
+ { 0x404, 0x84f, "Vodafone IN" },
+ { 0x404, 0x85f, "Reliance" },
+ { 0x404, 0x86f, "Vodafone IN" },
+ { 0x404, 0x90f, "AirTel" },
+ { 0x404, 0x91f, "AIRCEL" },
+ { 0x404, 0x92f, "AirTel" },
+ { 0x404, 0x93f, "AirTel" },
+ { 0x404, 0x96f, "AirTel" },
+ { 0x405, 0x05f, "Reliance" },
+ { 0x405, 0x10f, "Reliance" },
+ { 0x405, 0x13f, "Reliance" },
+ { 0x405, 0x025, "TATA DOCOMO" },
+ { 0x405, 0x026, "TATA DOCOMO" },
+ { 0x405, 0x027, "TATA DOCOMO" },
+ { 0x405, 0x029, "TATA DOCOMO" },
+ { 0x405, 0x030, "TATA DOCOMO" },
+ { 0x405, 0x031, "TATA DOCOMO" },
+ { 0x405, 0x032, "TATA DOCOMO" },
+ { 0x405, 0x034, "TATA DOCOMO" },
+ { 0x405, 0x035, "TATA DOCOMO" },
+ { 0x405, 0x036, "TATA DOCOMO" },
+ { 0x405, 0x037, "TATA DOCOMO" },
+ { 0x405, 0x038, "TATA DOCOMO" },
+ { 0x405, 0x039, "TATA DOCOMO" },
+ { 0x405, 0x041, "TATA DOCOMO" },
+ { 0x405, 0x042, "TATA DOCOMO" },
+ { 0x405, 0x043, "TATA DOCOMO" },
+ { 0x405, 0x044, "TATA DOCOMO" },
+ { 0x405, 0x045, "TATA DOCOMO" },
+ { 0x405, 0x046, "TATA DOCOMO" },
+ { 0x405, 0x047, "TATA DOCOMO" },
+ { 0x405, 0x51f, "AirTel" },
+ { 0x405, 0x52f, "AirTel" },
+ { 0x405, 0x54f, "AirTel" },
+ { 0x405, 0x56f, "AirTel" },
+ { 0x405, 0x66f, "Vodafone IN" },
+ { 0x405, 0x70f, "IDEA" },
+ { 0x405, 0x750, "Vodafone IN" },
+ { 0x405, 0x751, "Vodafone IN" },
+ { 0x405, 0x752, "Vodafone IN" },
+ { 0x405, 0x753, "Vodafone IN" },
+ { 0x405, 0x754, "Vodafone IN" },
+ { 0x405, 0x755, "Vodafone IN" },
+ { 0x405, 0x756, "Vodafone IN" },
+ { 0x405, 0x799, "IDEA" },
+ { 0x405, 0x800, "AIRCEL" },
+ { 0x405, 0x801, "AIRCEL" },
+ { 0x405, 0x802, "AIRCEL" },
+ { 0x405, 0x803, "AIRCEL" },
+ { 0x405, 0x804, "AIRCEL" },
+ { 0x405, 0x805, "AIRCEL" },
+ { 0x405, 0x806, "AIRCEL" },
+ { 0x405, 0x807, "AIRCEL" },
+ { 0x405, 0x808, "AIRCEL" },
+ { 0x405, 0x809, "AIRCEL" },
+ { 0x405, 0x810, "AIRCEL" },
+ { 0x405, 0x811, "AIRCEL" },
+ { 0x405, 0x812, "AIRCEL" },
+ { 0x405, 0x819, "Uninor" },
+ { 0x405, 0x818, "[Uninor]" },
+ { 0x405, 0x820, "Uninor" },
+ { 0x405, 0x821, "Uninor" },
+ { 0x405, 0x822, "Uninor" },
+ { 0x405, 0x880, "Uninor" },
+ { 0x405, 0x824, "Videocon Datacom" },
+ { 0x405, 0x834, "Videocon Datacom" },
+ { 0x405, 0x844, "Uninor" },
+ { 0x405, 0x845, "IDEA" },
+ { 0x405, 0x848, "IDEA" },
+ { 0x405, 0x850, "IDEA" },
+ { 0x405, 0x855, "Loop Mobile" },
+ { 0x405, 0x864, "Loop Mobile" },
+ { 0x405, 0x865, "Loop Mobile" },
+ { 0x405, 0x875, "Uninor" },
+ { 0x405, 0x881, "S Tel" },
+ { 0x405, 0x912, "Etisalat DB" },
+ { 0x405, 0x913, "Etisalat DB" },
+ { 0x405, 0x917, "Etisalat DB" },
+ { 0x405, 0x929, "Uninor" },
+ { 0x510, -1, "Indonesia" },
+ { 0x510, 0x00f, "PSN" },
+ { 0x510, 0x01f, "INDOSAT" },
+ { 0x510, 0x03f, "StarOne" },
+ { 0x510, 0x07f, "TelkomFlexi" },
+ { 0x510, 0x08f, "AXIS" },
+ { 0x510, 0x09f, "SMART" },
+ { 0x510, 0x10f, "Telkomsel" },
+ { 0x510, 0x11f, "XL" },
+ { 0x510, 0x20f, "TELKOMMobile" },
+ { 0x510, 0x21f, "IM3" },
+ { 0x510, 0x27f, "Ceria" },
+ { 0x510, 0x28f, "Fren/Hepi" },
+ { 0x510, 0x89f, "3" },
+ { 0x510, 0x99f, "Esia " },
+ { 0x432, -1, "Iran" },
+ { 0x432, 0x11f, "MCI" },
+ { 0x432, 0x14f, "TKC" },
+ { 0x432, 0x19f, "MTCE" },
+ { 0x432, 0x32f, "Taliya" },
+ { 0x432, 0x35f, "Irancell" },
+ { 0x418, -1, "Iraq" },
+ { 0x418, 0x20f, "Zain IQ" },
+ { 0x418, 0x30f, "Zain IQ" },
+ { 0x418, 0x05f, "Asia Cell" },
+ { 0x418, 0x40f, "Korek" },
+ { 0x418, 0x08f, "SanaTel" },
+// { 0x418, ?, "IRAQNA" },
+ { 0x272, -1, "Ireland" },
+ { 0x272, 0x01f, "Vodafone" },
+ { 0x272, 0x02f, "O2" },
+ { 0x272, 0x03f, "Meteor" },
+ { 0x272, 0x04f, "Access Telecom" },
+ { 0x272, 0x05f, "3" },
+ { 0x272, 0x07f, "Eircom" },
+ { 0x272, 0x09f, "Clever Communications" },
+ { 0x234, -1, "Isle of Man" },
+ { 0x234, 0x58f, "Pronto GSM" },
+ { 0x425, -1, "Israel" },
+ { 0x425, 0x01f, "Orange" },
+ { 0x425, 0x02f, "Cellcom" },
+ { 0x425, 0x03f, "Pelephone" },
+ { 0x425, 0x77f, "Mirs" },
+ { 0x222, -1, "Italy" },
+ { 0x222, 0x01f, "TIM" },
+ { 0x222, 0x02f, "Elsacom" },
+ { 0x222, 0x10f, "Vodafone" },
+ { 0x222, 0x30f, "RFI" },
+ { 0x222, 0x77f, "IPSE 2000" },
+ { 0x222, 0x88f, "Wind" },
+ { 0x222, 0x98f, "Blu" },
+ { 0x222, 0x99f, "3 Italia" },
+ { 0x612, -1, "Ivory Coast" },
+ { 0x612, 0x01f, "Cora de Comstar" },
+ { 0x612, 0x02f, "Moov" },
+ { 0x612, 0x03f, "Orange" },
+ { 0x612, 0x04f, "KoZ" },
+ { 0x612, 0x05f, "MTN" },
+ { 0x612, 0x06f, "ORICEL" },
+ { 0x338, -1, "Jamaica" },
+ { 0x338, 0x020, "LIME (formerly known as Cable & Wireless)" },
+ { 0x338, 0x050, "Digicel" },
+ { 0x338, 0x070, "Claro" },
+ { 0x338, 0x180, "LIME (formerly known as Cable & Wireless)" },
+ { 0x440, -1, "Japan" },
+ { 0x440, 0x00f, "eMobile" },
+ { 0x440, 0x01f, "NTT docomo" },
+ { 0x440, 0x02f, "NTT docomo" },
+ { 0x440, 0x03f, "NTT docomo" },
+ { 0x440, 0x04f, "SoftBank" },
+ { 0x440, 0x06f, "SoftBank" },
+ { 0x440, 0x07f, "KDDI" },
+ { 0x440, 0x08f, "KDDI" },
+ { 0x440, 0x09f, "NTT docomo" },
+ { 0x440, 0x10f, "NTT docomo" },
+ { 0x440, 0x11f, "NTT docomo" },
+ { 0x440, 0x12f, "NTT docomo" },
+ { 0x440, 0x13f, "NTT docomo" },
+ { 0x440, 0x14f, "NTT docomo" },
+ { 0x440, 0x15f, "NTT docomo" },
+ { 0x440, 0x16f, "NTT docomo" },
+ { 0x440, 0x17f, "NTT docomo" },
+ { 0x440, 0x18f, "NTT docomo" },
+ { 0x440, 0x19f, "NTT docomo" },
+ { 0x440, 0x20f, "SoftBank" },
+ { 0x440, 0x21f, "NTT docomo" },
+ { 0x440, 0x22f, "NTT docomo" },
+ { 0x440, 0x23f, "DoCoMo" },
+ { 0x440, 0x24f, "DoCoMo" },
+ { 0x440, 0x25f, "DoCoMo" },
+ { 0x440, 0x26f, "DoCoMo" },
+ { 0x440, 0x27f, "DoCoMo" },
+ { 0x440, 0x28f, "DoCoMo" },
+ { 0x440, 0x29f, "DoCoMo" },
+ { 0x440, 0x30f, "DoCoMo" },
+ { 0x440, 0x31f, "DoCoMo" },
+ { 0x440, 0x32f, "DoCoMo" },
+ { 0x440, 0x33f, "DoCoMo" },
+ { 0x440, 0x34f, "DoCoMo" },
+ { 0x440, 0x35f, "DoCoMo" },
+ { 0x440, 0x36f, "DoCoMo" },
+ { 0x440, 0x37f, "DoCoMo" },
+ { 0x440, 0x38f, "DoCoMo" },
+ { 0x440, 0x39f, "DoCoMo" },
+ { 0x440, 0x40f, "SoftBank" },
+ { 0x440, 0x41f, "SoftBank" },
+ { 0x440, 0x42f, "SoftBank" },
+ { 0x440, 0x43f, "SoftBank" },
+ { 0x440, 0x44f, "SoftBank" },
+ { 0x440, 0x45f, "SoftBank" },
+ { 0x440, 0x46f, "SoftBank" },
+ { 0x440, 0x47f, "SoftBank" },
+ { 0x440, 0x48f, "SoftBank" },
+ { 0x440, 0x49f, "DoCoMo" },
+ { 0x440, 0x50f, "KDDI" },
+ { 0x440, 0x51f, "KDDI" },
+ { 0x440, 0x52f, "KDDI" },
+ { 0x440, 0x53f, "KDDI" },
+ { 0x440, 0x54f, "KDDI" },
+ { 0x440, 0x55f, "KDDI" },
+ { 0x440, 0x56f, "KDDI" },
+ { 0x440, 0x58f, "DoCoMo" },
+ { 0x440, 0x60f, "DoCoMo" },
+ { 0x440, 0x61f, "DoCoMo" },
+ { 0x440, 0x62f, "DoCoMo" },
+ { 0x440, 0x63f, "DoCoMo" },
+ { 0x440, 0x64f, "DoCoMo" },
+ { 0x440, 0x65f, "DoCoMo" },
+ { 0x440, 0x66f, "DoCoMo" },
+ { 0x440, 0x67f, "DoCoMo" },
+ { 0x440, 0x68f, "DoCoMo" },
+ { 0x440, 0x69f, "DoCoMo" },
+ { 0x440, 0x70f, "au" },
+ { 0x440, 0x71f, "KDDI" },
+ { 0x440, 0x72f, "KDDI" },
+ { 0x440, 0x73f, "KDDI" },
+ { 0x440, 0x74f, "KDDI" },
+ { 0x440, 0x75f, "KDDI" },
+ { 0x440, 0x76f, "KDDI" },
+ { 0x440, 0x77f, "KDDI" },
+ { 0x440, 0x78f, "Okinawa Cellular Telephone" },
+ { 0x440, 0x79f, "KDDI" },
+ { 0x440, 0x80f, "TU-KA" },
+ { 0x440, 0x81f, "TU-KA" },
+ { 0x440, 0x82f, "TU-KA" },
+ { 0x440, 0x83f, "TU-KA" },
+ { 0x440, 0x84f, "TU-KA" },
+ { 0x440, 0x85f, "TU-KA" },
+ { 0x440, 0x86f, "TU-KA" },
+ { 0x440, 0x87f, "DoCoMo" },
+ { 0x440, 0x88f, "KDDI" },
+ { 0x440, 0x89f, "KDDI" },
+ { 0x440, 0x90f, "SoftBank" },
+ { 0x440, 0x92f, "SoftBank" },
+ { 0x440, 0x93f, "SoftBank" },
+ { 0x440, 0x94f, "SoftBank" },
+ { 0x440, 0x95f, "SoftBank" },
+ { 0x440, 0x96f, "SoftBank" },
+ { 0x440, 0x97f, "SoftBank" },
+ { 0x440, 0x98f, "SoftBank" },
+ { 0x440, 0x99f, "DoCoMo" },
+ { 0x234, -1, "Jersey" },
+ { 0x234, 0x50f, "JT-Wave" },
+ { 0x234, 0x55f, "Sure Mobile" },
+ { 0x234, 0x03f, "Airtel Vodafone" },
+ { 0x416, -1, "Jordan" },
+ { 0x416, 0x01f, "zain JO" },
+ { 0x416, 0x02f, "XPress Telecom" },
+ { 0x416, 0x03f, "Umniah" },
+ { 0x416, 0x77f, "Orange" },
+ { 0x401, -1, "Kazakhstan" },
+ { 0x401, 0x01f, "Beeline" },
+ { 0x401, 0x02f, "Kcell" },
+ { 0x401, 0x07f, "Dalacom" },
+ { 0x401, 0x77f, "Mobile Telecom Service" },
+ { 0x639, -1, "Kenya" },
+ { 0x639, 0x02f, "Safaricom" },
+ { 0x639, 0x03f, "Zain" },
+ { 0x639, 0x07f, "Orange Kenya" },
+ { 0x545, -1, "Kiribati" },
+ { 0x545, 0x09f, "Kiribati Frigate" },
+ { 0x467, -1, "North Korea" },
+ { 0x467, 0x193, "SUN NET" },
+ { 0x450, -1, "South Korea" },
+ { 0x450, 0x02f, "KT" },
+ { 0x450, 0x03f, "Power 017" },
+ { 0x450, 0x04f, "KT" },
+ { 0x450, 0x05f, "SKT" },
+ { 0x450, 0x06f, "LGT" },
+ { 0x450, 0x08f, "KT SHOW" },
+ { 0x212, -1, "Kosovo" },
+ { 0x212, 0x01f, "Vala" },
+ { 0x293, 0x41f, "iPKO" },
+ { 0x293, 0x41f, "D3 Mobile" },
+ { 0x212, 0x01f, "Z Mobile" },
+ { 0x419, -1, "Kuwait" },
+ { 0x419, 0x02f, "zain KW" },
+ { 0x419, 0x03f, "Wataniya" },
+ { 0x419, 0x04f, "Viva" },
+ { 0x437, -1, "Kyrgyzstan" },
+ { 0x437, 0x01f, "Beeline" },
+ { 0x437, 0x05f, "MegaCom" },
+ { 0x437, 0x09f, "O!" },
+ { 0x457, -1, "Laos" },
+ { 0x457, 0x01f, "LaoTel" },
+ { 0x457, 0x02f, "ETL" },
+ { 0x457, 0x03f, "Unitel" },
+ { 0x457, 0x08f, "Tigo" },
+ { 0x247, -1, "Latvia" },
+ { 0x247, 0x01f, "LMT" },
+ { 0x247, 0x02f, "Tele2" },
+ { 0x247, 0x03f, "TRIATEL" },
+ { 0x247, 0x05f, "Bite" },
+ { 0x247, 0x06f, "Rigatta" },
+ { 0x247, 0x07f, "MTS" },
+ { 0x247, 0x08f, "IZZI" },
+ { 0x247, 0x09f, "Camel Mobile" },
+ { 0x415, -1, "Lebanon" },
+ { 0x415, 0x01f, "Alfa" },
+ { 0x415, 0x03f, "MTC-Touch" },
+ { 0x651, -1, "Lesotho" },
+ { 0x651, 0x01f, "Vodacom" },
+ { 0x651, 0x02f, "Econet Ezin-cel" },
+ { 0x618, -1, "Liberia" },
+ { 0x618, 0x01f, "Lonestar Cell" },
+ { 0x618, 0x04f, "Comium" },
+ { 0x618, 0x20f, "LIBTELCO" },
+ { 0x606, -1, "Libya" },
+ { 0x606, 0x00f, "Libyana" },
+ { 0x606, 0x01f, "Madar" },
+ { 0x295, -1, "Liechtenstein" },
+ { 0x295, 0x01f, "Swisscom" },
+ { 0x295, 0x02f, "Orange" },
+ { 0x295, 0x05f, "FL1" },
+ { 0x295, 0x77f, "Tele 2" },
+ { 0x246, -1, "Lithuania" },
+ { 0x246, 0x01f, "Omnitel" },
+ { 0x246, 0x02f, "BITE" },
+ { 0x246, 0x03f, "Tele 2" },
+ { 0x270, -1, "Luxembourg" },
+ { 0x270, 0x01f, "LuxGSM" },
+ { 0x270, 0x77f, "Tango" },
+ { 0x270, 0x99f, "Orange" },
+ { 0x455, -1, "Macau" },
+ { 0x455, 0x00f, "SmarTone" },
+ { 0x455, 0x01f, "CTM" },
+ { 0x455, 0x02f, "China Telecom" },
+ { 0x455, 0x03f, "3" },
+ { 0x455, 0x04f, "CTM" },
+ { 0x455, 0x05f, "3" },
+ { 0x294, -1, "Republic of Macedonia" },
+ { 0x294, 0x01f, "T-Mobile MK" },
+ { 0x294, 0x02f, "ONE" },
+ { 0x294, 0x03f, "Vip MK" },
+ { 0x646, -1, "Madagascar" },
+ { 0x646, 0x01f, "Zain" },
+ { 0x646, 0x02f, "Orange" },
+ { 0x646, 0x03f, "Sacel" },
+ { 0x646, 0x04f, "Telma" },
+ { 0x650, -1, "Malawi" },
+ { 0x650, 0x01f, "TNM" },
+ { 0x650, 0x10f, "Zain" },
+ { 0x502, -1, "Malaysia" },
+ { 0x502, 0x12f, "Maxis" },
+ { 0x502, 0x13f, "Celcom" },
+ { 0x502, 0x16f, "DiGi" },
+ { 0x502, 0x17f, "Maxis" },
+ { 0x502, 0x18f, "U Mobile" },
+ { 0x502, 0x19f, "Celcom" },
+ { 0x472, -1, "Maldives" },
+ { 0x472, 0x01f, "Dhiraagu" },
+ { 0x472, 0x02f, "Wataniya" },
+ { 0x610, -1, "Mali" },
+ { 0x610, 0x01f, "Malitel" },
+ { 0x610, 0x02f, "Orange" },
+ { 0x278, -1, "Malta" },
+ { 0x278, 0x01f, "Vodafone" },
+ { 0x278, 0x21f, "GO" },
+ { 0x278, 0x77f, "Melita" },
+ { 0x000, -1, "Marshall Islands" },
+// { 0x000, ?, "?" },
+ { 0x340, -1, "Martinique" },
+ { 0x340, 0x01f, "Orange" },
+ { 0x340, 0x02f, "Outremer" },
+ { 0x340, 0x20f, "Digicel" },
+ { 0x609, -1, "Mauritania" },
+ { 0x609, 0x01f, "Mattel" },
+ { 0x609, 0x10f, "Mauritel" },
+ { 0x617, -1, "Mauritius" },
+ { 0x617, 0x01f, "Orange" },
+ { 0x617, 0x02f, "MTML" },
+ { 0x617, 0x10f, "Emtel" },
+ { 0x334, -1, "Mexico" },
+ { 0x334, 0x01f, "Nextel" },
+ { 0x334, 0x02f, "Telcel" },
+ { 0x334, 0x03f, "movistar" },
+ { 0x334, 0x04f, "Iusacell / Unefon" },
+ { 0x550, -1, "Federated States of Micronesia" },
+ { 0x550, 0x01f, "FSM Telecom" },
+ { 0x259, -1, "Moldova" },
+ { 0x259, 0x01f, "Orange" },
+ { 0x259, 0x02f, "Moldcell" },
+ { 0x259, 0x03f, "IDC" },
+ { 0x259, 0x03f, "Unit?" },
+ { 0x259, 0x04f, "Eventis" },
+ { 0x259, 0x05f, "Unit?" },
+ { 0x212, -1, "Monaco" },
+ { 0x212, 0x01f, "Office des Telephones" },
+ { 0x428, -1, "Mongolia" },
+ { 0x428, 0x99f, "MobiCom" },
+ { 0x428, 0x88f, "Unitel" },
+ { 0x428, 0x91f, "Skytel" },
+ { 0x428, 0x98f, "G.Mobile" },
+ { 0x297, -1, "Montenegro" },
+ { 0x297, 0x01f, "Telenor" },
+ { 0x297, 0x02f, "T-Mobile" },
+ { 0x297, 0x03f, "m:tel CG" },
+ { 0x604, -1, "Morocco" },
+ { 0x604, 0x00f, "Moditel" },
+ { 0x604, 0x01f, "IAM" },
+ { 0x604, 0x02f, "INWI" },
+ { 0x605, 0x03f, "yassine" },
+ { 0x643, -1, "Mozambique" },
+ { 0x643, 0x01f, "mCel" },
+ { 0x643, 0x04f, "Vodacom" },
+ { 0x414, -1, "Myanmar" },
+ { 0x414, 0x01f, "MPT" },
+ { 0x649, -1, "Namibia" },
+ { 0x649, 0x01f, "MTC" },
+ { 0x649, 0x02f, "switch" },
+ { 0x649, 0x03f, "Leo" },
+ { 0x536, -1, "Nauru" },
+ { 0x429, -1, "Nepal" },
+ { 0x429, 0x01f, "Namaste / NT Mobile" },
+ { 0x429, 0x02f, "Ncell" },
+ { 0x429, 0x03f, "Sky/C-Phone" },
+ { 0x204, -1, "Netherlands" },
+ { 0x204, 0x01f, "OneFoon" },
+ { 0x204, 0x02f, "Tele2" },
+ { 0x204, 0x03f, "Blyk" },
+ { 0x204, 0x04f, "Vodafone" },
+ { 0x204, 0x05f, "Elephant Talk" },
+ { 0x204, 0x06f, "Barablu Mobile" },
+ { 0x204, 0x07f, "Teleena" },
+ { 0x204, 0x08f, "KPN" },
+ { 0x204, 0x09f, "Lycamobile" },
+ { 0x204, 0x10f, "KPN" },
+ { 0x204, 0x12f, "Telfort" },
+ { 0x204, 0x14f, "6Gmobile" },
+ { 0x204, 0x16f, "T-Mobile" },
+ { 0x204, 0x18f, "Telfort" },
+ { 0x204, 0x20f, "Orange Nederland" },
+ { 0x204, 0x21f, "NS Railinfrabeheer B.V." },
+ { 0x204, 0x67f, "RadioAccess" },
+ { 0x204, 0x69f, "KPN Mobile" },
+ { 0x362, -1, "Netherlands Antilles" },
+ { 0x362, 0x51f, "Telcell" },
+ { 0x362, 0x69f, "Digicel" },
+ { 0x362, 0x91f, "UTS" },
+ { 0x362, 0x00f, "East Caribbean Cellular" },
+ { 0x362, 0x00f, "Antiliano Por N.V." },
+ { 0x362, 0x95f, "MIO" },
+ { 0x362, 0x94f, "Bay?s" },
+ { 0x546, -1, "New Caledonia" },
+ { 0x546, 0x01f, "Mobilis" },
+ { 0x530, -1, "New Zealand" },
+ { 0x530, 0x00f, "Telecom" },
+ { 0x530, 0x01f, "Vodafone" },
+ { 0x530, 0x02f, "Telecom" },
+ { 0x530, 0x03f, "Woosh" },
+ { 0x530, 0x04f, "TelstraClear" },
+ { 0x530, 0x05f, "XT Mobile Network" },
+ { 0x530, 0x12f, "360" },
+ { 0x530, 0x24f, "2degrees" },
+ { 0x710, -1, "Nicaragua" },
+ { 0x710, 0x21f, "Claro" },
+ { 0x710, 0x30f, "movistar" },
+ { 0x710, 0x73f, "SERCOM" },
+ { 0x614, -1, "Niger" },
+ { 0x614, 0x01f, "SahelCom" },
+ { 0x614, 0x02f, "Zain" },
+ { 0x614, 0x03f, "Telecel" },
+ { 0x614, 0x04f, "Orange" },
+ { 0x621, -1, "Nigeria" },
+ { 0x621, 0x20f, "Zain" },
+ { 0x621, 0x30f, "MTN" },
+ { 0x621, 0x40f, "M-Tel" },
+ { 0x621, 0x50f, "Glo" },
+ { 0x621, 0x60f, "Etisalat" },
+ { 0x242, -1, "Norway" },
+ { 0x242, 0x01f, "Telenor" },
+ { 0x242, 0x02f, "NetCom" },
+ { 0x242, 0x03f, "Teletopia" },
+ { 0x242, 0x04f, "Tele2" },
+ { 0x242, 0x05f, "Network Norway" },
+ { 0x242, 0x06f, "Ice" },
+ { 0x242, 0x07f, "Ventelo" },
+ { 0x242, 0x08f, "TDC Mobil AS" },
+ { 0x242, 0x09f, "Barablu Mobile Norway Ltd" },
+ { 0x242, 0x20f, "Jernbaneverket AS" },
+ { 0x422, -1, "Oman" },
+ { 0x422, 0x02f, "Oman Mobile" },
+ { 0x422, 0x03f, "Nawras" },
+ { 0x410, -1, "Pakistan" },
+ { 0x410, 0x01f, "Mobilink" },
+ { 0x410, 0x03f, "Ufone" },
+ { 0x410, 0x04f, "Zong" },
+ { 0x410, 0x06f, "Telenor" },
+ { 0x410, 0x07f, "Warid" },
+ { 0x552, -1, "Palau" },
+ { 0x552, 0x01f, "PNCC" },
+ { 0x552, 0x80f, "Palau Mobile" },
+ { 0x423, -1, "Palestinian Authority" },
+ { 0x423, 0x05f, "Jawwal" },
+ { 0x423, 0x06f, "Wataniya" },
+ { 0x714, -1, "Panama" },
+ { 0x714, 0x01f, "Cable & Wireless" },
+ { 0x714, 0x02f, "movistar" },
+ { 0x714, 0x04f, "Digicel" },
+ { 0x714, 0x03f, "Claro" },
+ { 0x537, -1, "Papua New Guinea" },
+ { 0x537, 0x01f, "B-Mobile" },
+ { 0x537, 0x03f, "Digicel" },
+ { 0x744, -1, "Paraguay" },
+ { 0x744, 0x01f, "VOX" },
+ { 0x744, 0x02f, "Claro" },
+ { 0x744, 0x04f, "Tigo" },
+ { 0x744, 0x05f, "Personal" },
+ { 0x716, -1, "Peru" },
+ { 0x716, 0x06f, "movistar" },
+ { 0x716, 0x10f, "Claro" },
+ { 0x716, 0x17f, "NEXTEL" },
+ { 0x515, -1, "Philippines" },
+ { 0x515, 0x01f, "Islacom" },
+ { 0x515, 0x02f, "Globe" },
+ { 0x515, 0x03f, "Smart" },
+ { 0x515, 0x05f, "Sun" },
+ { 0x515, 0x11f, "PLDT via ACeS Philippines" },
+ { 0x515, 0x18f, "Cure" },
+ { 0x515, 0x88f, "Nextel" },
+ { 0x260, -1, "Poland" },
+ { 0x260, 0x01f, "Plus" },
+ { 0x260, 0x02f, "Era" },
+ { 0x260, 0x03f, "Orange" },
+ { 0x260, 0x04f, "Netia S.A." },
+ { 0x260, 0x05f, "Polska Telefonia Kom?rkowa Centertel Sp. z o.o." },
+ { 0x260, 0x06f, "Play" },
+ { 0x260, 0x07f, "Netia" },
+ { 0x260, 0x08f, "E-Telko Sp. z o.o." },
+ { 0x260, 0x09f, "Telekomunikacja Kolejowa Sp. z o.o." },
+ { 0x260, 0x10f, "Sferia" },
+ { 0x260, 0x11f, "Nordisk Polska" },
+ { 0x260, 0x12f, "Cyfrowy Polsat" },
+ { 0x260, 0x13f, "Sferia" },
+ { 0x260, 0x14f, "Sferia" },
+ { 0x260, 0x15f, "CenterNet" },
+ { 0x260, 0x16f, "Mobyland" },
+ { 0x260, 0x17f, "Aero2" },
+ { 0x268, -1, "Portugal" },
+ { 0x268, 0x01f, "Vodafone" },
+ { 0x268, 0x03f, "Optimus" },
+ { 0x268, 0x06f, "TMN" },
+ { 0x268, 0x21f, "Zapp" },
+ { 0x330, -1, "Puerto Rico" },
+ { 0x330, 0x11f, "Claro" },
+ { 0x427, -1, "Qatar" },
+ { 0x427, 0x01f, "Qatarnet" },
+ { 0x427, 0x02f, "Vodafone Qatar" },
+ { 0x647, -1, "R&?union" },
+ { 0x647, 0x00f, "Orange" },
+ { 0x647, 0x02f, "Outremer" },
+ { 0x647, 0x10f, "SFR Reunion" },
+ { 0x226, -1, "Romania" },
+ { 0x226, 0x01f, "Vodafone" },
+ { 0x226, 0x02f, "Romtelecom" },
+ { 0x226, 0x03f, "Cosmote" },
+ { 0x226, 0x04f, "Cosmote" },
+ { 0x226, 0x05f, "Digi.Mobil" },
+ { 0x226, 0x06f, "Cosmote" },
+ { 0x226, 0x10f, "Orange" },
+ { 0x250, -1, "Russian Federation" },
+ { 0x250, 0x01f, "MTS" },
+ { 0x250, 0x02f, "MegaFon" },
+ { 0x250, 0x03f, "NCC" },
+ { 0x250, 0x04f, "Sibchallenge" },
+ { 0x250, 0x05f, "ETK" },
+ { 0x250, 0x06f, "Skylink" },
+ { 0x250, 0x07f, "SMARTS" },
+ { 0x250, 0x09f, "Skylink" },
+ { 0x250, 0x10f, "DTC" },
+ { 0x250, 0x11f, "Orensot" },
+ { 0x250, 0x12f, "Baykalwestcom" },
+ { 0x250, 0x12f, "Akos" },
+ { 0x250, 0x13f, "KUGSM" },
+ { 0x250, 0x15f, "SMARTS" },
+ { 0x250, 0x16f, "NTC" },
+ { 0x250, 0x17f, "Utel" },
+ { 0x250, 0x19f, "INDIGO" },
+ { 0x250, 0x20f, "Tele2" },
+ { 0x250, 0x23f, "Mobicom - Novosibirsk" },
+ { 0x250, 0x28f, "Beeline" },
+ { 0x250, 0x35f, "MOTIV" },
+ { 0x250, 0x38f, "Tambov GSM" },
+ { 0x250, 0x39f, "Utel" },
+ { 0x250, 0x44f, "Stavtelesot / North Caucasian GSM" },
+ { 0x250, 0x92f, "Primtelefon" },
+ { 0x250, 0x93f, "Telecom XXI" },
+ { 0x250, 0x99f, "Beeline" },
+// { 0x250, ?, "SkyLink/MTS/the Moscow Cellular communication" },
+ { 0x635, -1, "Rwanda" },
+ { 0x635, 0x10f, "MTN" },
+ { 0x635, 0x13f, "Tigo" },
+ { 0x356, -1, "Saint Kitts and Nevis" },
+ { 0x356, 0x050, "Digicel" },
+ { 0x356, 0x110, "Cable & Wireless" },
+ { 0x358, -1, "Saint Lucia" },
+ { 0x358, 0x050, "Digicel" },
+ { 0x358, 0x110, "Cable & Wireless" },
+ { 0x308, -1, "Saint Pierre and Miquelon" },
+ { 0x308, 0x01f, "Ameris" },
+ { 0x360, -1, "Saint Vincent and the Grenadines" },
+ { 0x360, 0x070, "Digicel" },
+ { 0x360, 0x100, "Cingular Wireless" },
+ { 0x360, 0x110, "Cable & Wireless" },
+ { 0x549, -1, "Samoa" },
+ { 0x549, 0x01f, "Digicel" },
+ { 0x549, 0x27f, "SamoaTel" },
+ { 0x292, -1, "San Marino" },
+ { 0x292, 0x01f, "PRIMA" },
+ { 0x626, -1, "Sao Tome and Principe" },
+ { 0x626, 0x01f, "CSTmovel" },
+ { 0x420, -1, "Saudi Arabia" },
+ { 0x420, 0x01f, "Al Jawal" },
+ { 0x420, 0x03f, "Mobily" },
+ { 0x420, 0x07f, "EAE" },
+ { 0x420, 0x04f, "Zain SA" },
+ { 0x608, -1, "Senegal" },
+ { 0x608, 0x01f, "Orange (telecommunications)" },
+ { 0x608, 0x02f, "Tigo" },
+ { 0x608, 0x03f, "Expresso" },
+ { 0x220, -1, "Serbia" },
+ { 0x220, 0x01f, "Telenor" },
+ { 0x220, 0x03f, "mt:s" },
+ { 0x220, 0x05f, "VIP" },
+ { 0x633, -1, "Seychelles" },
+ { 0x633, 0x01f, "Cable & Wireless" },
+ { 0x633, 0x02f, "Mediatech International" },
+ { 0x633, 0x10f, "Airtel" },
+ { 0x619, -1, "Sierra Leone" },
+ { 0x619, 0x01f, "Zain" },
+ { 0x619, 0x02f, "Millicom" },
+ { 0x619, 0x03f, "Datatel" },
+ { 0x619, 0x04f, "Comium" },
+ { 0x619, 0x05f, "Africell" },
+ { 0x619, 0x25f, "Mobitel" },
+// { 0x619, ?, "LeoneCel" },
+ { 0x525, -1, "Singapore" },
+ { 0x525, 0x01f, "SingTel" },
+ { 0x525, 0x02f, "SingTel-G18" },
+ { 0x525, 0x03f, "M1" },
+ { 0x525, 0x05f, "StarHub" },
+ { 0x525, 0x12f, "Digital Trunked Radio Network" },
+ { 0x231, -1, "Slovakia" },
+ { 0x231, 0x01f, "Orange" },
+ { 0x231, 0x02f, "T-Mobile" },
+ { 0x231, 0x03f, "Unient Communications" },
+ { 0x231, 0x04f, "T-Mobile" },
+ { 0x231, 0x05f, "Mobile Entertainment Company" },
+ { 0x231, 0x06f, "O2" },
+ { 0x231, 0x99f, "?SR" },
+ { 0x293, -1, "Slovenia" },
+ { 0x293, 0x40f, "Si.mobil" },
+ { 0x293, 0x41f, "Mobitel" },
+ { 0x293, 0x64f, "T-2" },
+ { 0x293, 0x70f, "Tu?mobil" },
+ { 0x540, -1, "Solomon Islands" },
+ { 0x637, -1, "Somalia" },
+ { 0x637, 0x01f, "Telesom" },
+ { 0x637, 0x04f, "Somafone" },
+ { 0x637, 0x10f, "Nationlink" },
+ { 0x637, 0x25f, "Hormuud" },
+ { 0x637, 0x30f, "Golis" },
+ { 0x637, 0x82f, "Telcom" },
+ { 0x655, -1, "South Africa" },
+ { 0x655, 0x01f, "Vodacom" },
+ { 0x655, 0x06f, "Sentech" },
+ { 0x655, 0x07f, "Cell C" },
+ { 0x655, 0x10f, "MTN" },
+ { 0x655, 0x11f, "SAPS Gauteng" },
+ { 0x655, 0x13f, "Neotel" },
+ { 0x655, 0x21f, "Cape Town Metropolitan Council" },
+ { 0x655, 0x30f, "Bokamoso Consortium" },
+ { 0x655, 0x31f, "Karabo Telecoms (Pty) Ltd." },
+ { 0x655, 0x32f, "Ilizwi Telecommunications" },
+ { 0x655, 0x33f, "Thinta Thinta Telecommunications" },
+ { 0x655, 0x02f, "Telkom" },
+ { 0x214, -1, "Spain" },
+ { 0x214, 0x01f, "Vodafone" },
+ { 0x214, 0x03f, "Orange" },
+ { 0x214, 0x04f, "Yoigo" },
+ { 0x214, 0x05f, "TME" },
+ { 0x214, 0x06f, "Vodafone" },
+ { 0x214, 0x07f, "movistar" },
+ { 0x214, 0x08f, "Euskaltel" },
+ { 0x214, 0x09f, "Orange" },
+ { 0x214, 0x15f, "BT" },
+ { 0x214, 0x16f, "TeleCable" },
+ { 0x214, 0x17f, "M?bil R" },
+ { 0x214, 0x18f, "ONO" },
+ { 0x214, 0x19f, "Simyo" },
+ { 0x214, 0x21f, "Jazztel" },
+ { 0x214, 0x22f, "DigiMobil" },
+ { 0x214, 0x23f, "Barablu" },
+ { 0x413, -1, "Sri Lanka" },
+ { 0x413, 0x01f, "Mobitel" },
+ { 0x413, 0x02f, "Dialog" },
+ { 0x413, 0x03f, "Etisalat" },
+ { 0x413, 0x05f, "Airtel" },
+ { 0x413, 0x08f, "Hutch" },
+ { 0x413, 0x00f, "RTEC Mobile" },
+ { 0x634, -1, "Sudan" },
+ { 0x634, 0x01f, "Zain SD" },
+ { 0x634, 0x02f, "MTN" },
+ { 0x634, 0x05f, "Vivacell" },
+ { 0x746, -1, "Suriname" },
+ { 0x746, 0x05f, "Telesur" },
+ { 0x653, -1, "Swaziland" },
+ { 0x653, 0x10f, "Swazi MTN" },
+ { 0x240, -1, "Sweden" },
+ { 0x240, 0x01f, "Telia" },
+ { 0x240, 0x02f, "3" },
+ { 0x240, 0x03f, "Ice.net" },
+ { 0x240, 0x04f, "3G Infrastructure Services" },
+ { 0x240, 0x05f, "Sweden 3G" },
+ { 0x240, 0x06f, "Telenor" },
+ { 0x240, 0x07f, "Tele2" },
+ { 0x240, 0x08f, "Telenor" },
+ { 0x240, 0x09f, "djuice" },
+ { 0x240, 0x10f, "Spring Mobil" },
+ { 0x240, 0x11f, "Lindholmen Science Park" },
+ { 0x240, 0x12f, "Barablu Mobile Scandinavia" },
+ { 0x240, 0x13f, "Ventelo Sverige" },
+ { 0x240, 0x14f, "TDC Mobil" },
+ { 0x240, 0x15f, "Wireless Maingate Nordic" },
+ { 0x240, 0x16f, "42IT" },
+ { 0x240, 0x17f, "G?talandsn?tet" },
+ { 0x240, 0x20f, "Wireless Maingate Message Services" },
+ { 0x240, 0x21f, "MobiSir" },
+ { 0x240, 0x25f, "DigiTelMobile" },
+ { 0x228, -1, "Switzerland" },
+ { 0x228, 0x01f, "Swisscom" },
+ { 0x228, 0x02f, "Sunrise" },
+ { 0x228, 0x03f, "Orange" },
+ { 0x228, 0x05f, "Togewanet AG (Comfone)" },
+ { 0x228, 0x06f, "SBB AG" },
+ { 0x228, 0x07f, "IN&Phone" },
+ { 0x228, 0x08f, "Tele2" },
+ { 0x228, 0x50f, "3G Mobile AG" },
+ { 0x228, 0x51f, "BebbiCell AG" },
+ { 0x417, -1, "Syria" },
+ { 0x417, 0x01f, "Syriatel" },
+ { 0x417, 0x02f, "MTN" },
+ { 0x466, -1, "Taiwan" },
+ { 0x466, 0x01f, "FarEasTone" },
+ { 0x466, 0x02f, "APTG" },
+ { 0x466, 0x06f, "Tuntex" },
+ { 0x466, 0x11f, "Chunghwa LDM" },
+ { 0x466, 0x88f, "KG Telecom" },
+ { 0x466, 0x89f, "VIBO" },
+ { 0x466, 0x92f, "Chungwa" },
+ { 0x466, 0x93f, "MobiTai" },
+ { 0x466, 0x97f, "Taiwan Mobile" },
+ { 0x466, 0x99f, "TransAsia" },
+ { 0x436, -1, "Tajikistan" },
+ { 0x436, 0x01f, "Tcell" },
+ { 0x436, 0x02f, "Indigo" },
+ { 0x436, 0x03f, "MLT" },
+ { 0x436, 0x04f, "Babilon-M" },
+ { 0x436, 0x05f, "Beeline" },
+ { 0x640, -1, "Tanzania" },
+ { 0x640, 0x06f, "SasaTel" },
+ { 0x640, 0x02f, "tiGO" },
+ { 0x640, 0x03f, "Zantel" },
+ { 0x640, 0x04f, "Vodacom" },
+ { 0x640, 0x05f, "Zain" },
+ { 0x520, -1, "Thailand" },
+ { 0x520, 0x00f, "Hutch" },
+ { 0x520, 0x01f, "AIS" },
+ { 0x520, 0x02f, "CAT CDMA" },
+ { 0x520, 0x10f, "?" },
+ { 0x520, 0x15f, "Thai Mobile" },
+ { 0x520, 0x15f, "TOT 3G" },
+ { 0x520, 0x18f, "dtac" },
+ { 0x520, 0x23f, "AIS GSM 1800" },
+ { 0x520, 0x99f, "True Move" },
+ { 0x520, 0x00f, "WE PCT" },
+ { 0x615, -1, "Togo" },
+ { 0x615, 0x01f, "Togo Cell" },
+ { 0x615, 0x03f, "Moov" },
+ { 0x539, -1, "Tonga" },
+ { 0x539, 0x01f, "Tonga Communications Corporation" },
+ { 0x539, 0x43f, "Shoreline Communication" },
+ { 0x539, 0x88f, "Digicel" },
+ { 0x374, -1, "Trinidad and Tobago" },
+ { 0x374, 0x12f, "bmobile" },
+ { 0x374, 0x13f, "Digicel" },
+ { 0x605, -1, "Tunisia" },
+ { 0x605, 0x01f, "Orange" },
+ { 0x605, 0x02f, "Tunicell" },
+ { 0x605, 0x03f, "Tunisiana" },
+ { 0x286, -1, "Turkey" },
+ { 0x286, 0x01f, "Turkcell" },
+ { 0x286, 0x02f, "Vodafone" },
+ { 0x286, 0x03f, "Avea" },
+ { 0x286, 0x04f, "Aycell" },
+ { 0x438, -1, "Turkmenistan" },
+ { 0x438, 0x01f, "MTS" },
+ { 0x438, 0x02f, "TM-Cell" },
+ { 0x376, -1, "Turks and Caicos Islands" },
+ { 0x376, 0x350, "C&W" },
+ { 0x376, 0x352, "Islandcom" },
+ { 0x338, 0x05f, "Digicel" },
+ { 0x553, -1, "Tuvalu" },
+ { 0x553, 0x01f, "TTC" },
+ { 0x641, -1, "Uganda" },
+ { 0x641, 0x01f, "Zain" },
+ { 0x641, 0x10f, "MTN" },
+ { 0x641, 0x11f, "Uganda Telecom Ltd." },
+ { 0x641, 0x22f, "Warid Telecom" },
+ { 0x641, 0x14f, "Orange" },
+ { 0x255, -1, "Ukraine" },
+ { 0x255, 0x01f, "MTS" },
+ { 0x255, 0x02f, "Beeline" },
+ { 0x255, 0x03f, "Kyivstar" },
+ { 0x255, 0x04f, "IT" },
+ { 0x255, 0x05f, "Golden Telecom" },
+ { 0x255, 0x06f, "life:)" },
+ { 0x255, 0x07f, "Ukrtelecom" },
+ { 0x255, 0x21f, "PEOPLEnet" },
+ { 0x255, 0x23f, "CDMA Ukraine" },
+ { 0x424, -1, "United Arab Emirates" },
+ { 0x424, 0x02f, "Etisalat" },
+ { 0x424, 0x03f, "du" },
+ { 0x234, -1, "United Kingdom" },
+ { 0x234, 0x00f, "BT" },
+ { 0x234, 0x01f, "UK01" },
+ { 0x234, 0x02f, "O2" },
+ { 0x234, 0x03f, "Airtel-Vodafone" },
+ { 0x234, 0x04f, "FMS Solutions Ltd" },
+ { 0x234, 0x07f, "Cable and Wireless UK" },
+ { 0x234, 0x08f, "OnePhone Ltd" },
+ { 0x234, 0x10f, "O2" },
+ { 0x234, 0x11f, "O2" },
+ { 0x234, 0x12f, "Railtrack" },
+ { 0x234, 0x14f, "Hay Systems Ltd" },
+ { 0x234, 0x15f, "Vodafone" },
+ { 0x234, 0x16f, "Opal Telecom Ltd" },
+ { 0x234, 0x18f, "Cloud9" },
+ { 0x234, 0x19f, "Teleware" },
+ { 0x234, 0x20f, "3" },
+ { 0x234, 0x22f, "RoutoMessaging" },
+ { 0x234, 0x25f, "Truphone" },
+ { 0x234, 0x30f, "T-Mobile" },
+ { 0x234, 0x31f, "Virgin" },
+ { 0x234, 0x32f, "Virgin" },
+ { 0x234, 0x33f, "Orange" },
+ { 0x234, 0x34f, "Orange" },
+ { 0x234, 0x50f, "JT-Wave" },
+ { 0x234, 0x55f, "Cable & Wireless Guernsey / Sure Mobile (Jersey)" },
+ { 0x234, 0x58f, "Manx Telecom" },
+ { 0x234, 0x75f, "Inquam" },
+ { 0x234, 0x77f, "BT" },
+ { 0x200, -1, "United States of America" },
+ { 0x200, 0x053, "Virgin Mobile US" },
+ { 0x200, 0x054, "Alltel US" },
+ { 0x200, 0x066, "U.S. Cellular" },
+ { 0x310, 0x00f, "nTelos" },
+ { 0x310, 0x000, "Mid-Tex Cellular" },
+ { 0x310, 0x004, "Verizon" },
+ { 0x310, 0x010, "MCI" },
+ { 0x310, 0x012, "Verizon" },
+ { 0x310, 0x013, "MobileTel" },
+ { 0x310, 0x014, "Testing" },
+ { 0x310, 0x016, "Cricket Communications" },
+ { 0x310, 0x017, "North Sight Communications Inc." },
+ { 0x310, 0x020, "Union Telephone Company" },
+ { 0x310, 0x026, "T-Mobile" },
+ { 0x310, 0x030, "Centennial" },
+ { 0x310, 0x034, "Airpeak" },
+ { 0x310, 0x038, "AT&T" },
+ { 0x310, 0x040, "Concho" },
+ { 0x310, 0x046, "SIMMETRY" },
+ { 0x310, 0x060, "Consolidated Telcom" },
+ { 0x310, 0x070, "Highland Cellular" },
+ { 0x310, 0x080, "Corr" },
+ { 0x310, 0x090, "AT&T" },
+ { 0x310, 0x100, "Plateau Wireless" },
+ { 0x310, 0x110, "PTI Pacifica" },
+ { 0x310, 0x120, "Sprint" },
+ { 0x310, 0x150, "AT&T" },
+ { 0x310, 0x160, "T-Mobile" },
+ { 0x310, 0x170, "T-Mobile" },
+ { 0x310, 0x180, "West Central" },
+ { 0x310, 0x190, "Dutch Harbor" },
+ { 0x310, 0x200, "T-Mobile" },
+ { 0x310, 0x210, "T-Mobile" },
+ { 0x310, 0x220, "T-Mobile" },
+ { 0x310, 0x230, "T-Mobile" },
+ { 0x310, 0x240, "T-Mobile" },
+ { 0x310, 0x250, "T-Mobile" },
+ { 0x310, 0x260, "T-Mobile" },
+ { 0x310, 0x270, "T-Mobile" },
+ { 0x310, 0x280, "T-Mobile" },
+ { 0x310, 0x290, "T-Mobile" },
+ { 0x310, 0x300, "iSmart Mobile" },
+ { 0x310, 0x310, "T-Mobile" },
+ { 0x310, 0x311, "Farmers Wireless" },
+ { 0x310, 0x320, "Cellular One" },
+ { 0x310, 0x330, "T-Mobile" },
+ { 0x310, 0x340, "Westlink" },
+ { 0x310, 0x350, "Carolina Phone" },
+ { 0x310, 0x380, "AT&T Mobility" },
+ { 0x310, 0x390, "Cellular One of East Texas" },
+ { 0x310, 0x400, "i CAN_GSM" },
+ { 0x310, 0x410, "AT&T" },
+ { 0x310, 0x420, "Cincinnati Bell" },
+ { 0x310, 0x430, "Alaska Digitel" },
+ { 0x310, 0x440, "Cellular One" },
+ { 0x310, 0x450, "Viaero" },
+ { 0x310, 0x460, "Simmetry" },
+ { 0x310, 0x480, "Choice Phone" },
+ { 0x310, 0x490, "T-Mobile" },
+ { 0x310, 0x500, "Alltel" },
+ { 0x310, 0x510, "Airtel" },
+ { 0x310, 0x520, "VeriSign" },
+ { 0x310, 0x530, "West Virginia Wireless" },
+ { 0x310, 0x540, "Oklahoma Western" },
+ { 0x310, 0x560, "AT&T" },
+ { 0x310, 0x570, "Cellular One" },
+ { 0x310, 0x580, "T-Mobile" },
+ { 0x310, 0x590, "Alltel" },
+ { 0x310, 0x610, "Epic Touch" },
+ { 0x310, 0x620, "Coleman County Telecom" },
+ { 0x310, 0x630, "AmeriLink PCS" },
+ { 0x310, 0x640, "Airadigm" },
+ { 0x310, 0x650, "Jasper" },
+ { 0x310, 0x660, "T-Mobile" },
+ { 0x310, 0x670, "Northstar" },
+ { 0x310, 0x680, "AT&T" },
+ { 0x310, 0x690, "Conestoga" },
+ { 0x310, 0x730, "SeaMobile" },
+ { 0x310, 0x740, "Convey" },
+ { 0x310, 0x760, "Panhandle" },
+ { 0x310, 0x770, "i wireless" },
+ { 0x310, 0x780, "Airlink PCS" },
+ { 0x310, 0x790, "PinPoint" },
+ { 0x310, 0x800, "T-Mobile" },
+ { 0x310, 0x830, "Caprock" },
+ { 0x310, 0x850, "Aeris" },
+ { 0x310, 0x870, "PACE" },
+ { 0x310, 0x880, "Advantage" },
+ { 0x310, 0x890, "Unicel" },
+ { 0x310, 0x900, "Mid-Rivers Wireless" },
+ { 0x310, 0x910, "First Cellular" },
+ { 0x310, 0x940, "Iris Wireless LLC" },
+ { 0x310, 0x950, "XIT Wireless" },
+ { 0x310, 0x960, "Plateau Wireless" },
+ { 0x310, 0x970, "Globalstar" },
+ { 0x310, 0x980, "AT&T Mobility" },
+ { 0x310, 0x990, "AT&T Mobility" },
+ { 0x311, 0x000, "Mid-Tex Cellular" },
+ { 0x311, 0x010, "Chariton Valley" },
+ { 0x311, 0x020, "Missouri RSA 5 Partnership" },
+ { 0x311, 0x030, "Indigo Wireless" },
+ { 0x311, 0x040, "Commnet Wireless" },
+ { 0x311, 0x050, "Wikes Cellular" },
+ { 0x311, 0x060, "Farmers Cellular" },
+ { 0x311, 0x070, "Easterbrooke" },
+ { 0x311, 0x080, "Pine Cellular" },
+ { 0x311, 0x090, "Long Lines Wireless" },
+ { 0x311, 0x100, "High Plains Wireless" },
+ { 0x311, 0x110, "High Plains Wireless" },
+ { 0x311, 0x120, "Choice Phone" },
+ { 0x311, 0x130, "Cell One Amarillo" },
+ { 0x311, 0x140, "Sprocket" },
+ { 0x311, 0x150, "Wilkes Cellular" },
+ { 0x311, 0x160, "Endless Mountains Wireless" },
+ { 0x311, 0x170, "PetroCom" },
+ { 0x311, 0x180, "Cingular Wireless" },
+ { 0x311, 0x190, "Cellular Properties" },
+ { 0x311, 0x210, "Farmers Cellular" },
+ { 0x316, 0x010, "Nextel" },
+ { 0x316, 0x011, "Southern Communications Services" },
+ { 0x748, -1, "Uruguay" },
+ { 0x748, 0x00f, "Ancel" },
+ { 0x748, 0x01f, "Ancel" },
+ { 0x748, 0x07f, "Movistar" },
+ { 0x748, 0x10f, "Claro" },
+ { 0x434, -1, "Uzbekistan" },
+ { 0x434, 0x01f, "Buztel" },
+ { 0x434, 0x02f, "Uzmacom" },
+ { 0x434, 0x04f, "Beeline" },
+ { 0x434, 0x05f, "Ucell" },
+ { 0x434, 0x06f, "Perfectum Mobile" },
+ { 0x434, 0x07f, "MTS" },
+ { 0x541, -1, "Vanuatu" },
+ { 0x541, 0x01f, "SMILE" },
+ { 0x225, -1, "Vatican" },
+ { 0x734, -1, "Venezuela" },
+ { 0x734, 0x01f, "Digitel" },
+ { 0x734, 0x02f, "Digitel" },
+ { 0x734, 0x03f, "Digitel" },
+ { 0x734, 0x04f, "movistar" },
+ { 0x734, 0x06f, "Movilnet" },
+ { 0x452, -1, "Vietnam" },
+ { 0x452, 0x01f, "MobiFone" },
+ { 0x452, 0x02f, "Vinaphone" },
+ { 0x452, 0x03f, "S-Fone" },
+ { 0x452, 0x04f, "Viettel Mobile" },
+ { 0x452, 0x05f, "Vietnamobile" },
+ { 0x452, 0x06f, "E-Mobile" },
+ { 0x452, 0x07f, "Beeline VN" },
+ { 0x421, -1, "Yemen" },
+ { 0x421, 0x01f, "SabaFon" },
+ { 0x421, 0x02f, "MTN" },
+ { 0x421, 0x03f, "Yemen Mobile" },
+ { 0x421, 0x04f, "HiTS-UNITEL" },
+ { 0x645, -1, "Zambia" },
+ { 0x645, 0x01f, "Zain" },
+ { 0x645, 0x02f, "MTN" },
+ { 0x645, 0x03f, "ZAMTEL" },
+ { 0x648, -1, "Zimbabwe" },
+ { 0x648, 0x01f, "Net*One" },
+ { 0x648, 0x03f, "Telecel" },
+ { 0x648, 0x04f, "Econet" },
+ { 0x901, -1, "International" },
+ { 0x901, 0x01f, "ICO" },
+ { 0x901, 0x02f, "Sense Communications International" },
+ { 0x901, 0x03f, "Iridium" },
+ { 0x901, 0x04f, "Globalstar" },
+ { 0x901, 0x05f, "Thuraya RMSS Network" },
+ { 0x901, 0x06f, "Thuraya Satellite Telecommunications Company" },
+ { 0x901, 0x07f, "Ellipso" },
+ { 0x901, 0x08f, "" },
+ { 0x901, 0x09f, "Tele1 Europe" },
+ { 0x901, 0x10f, "ACeS" },
+ { 0x901, 0x11f, "Inmarsat" },
+ { 0x901, 0x12f, "MCP" },
+ { 0x901, 0x13f, "GSM.AQ" },
+ { 0x901, 0x14f, "AeroMobile AS" },
+ { 0x901, 0x15f, "OnAir Switzerland Sarl" },
+ { 0x901, 0x16f, "Jasper Systems" },
+ { 0x901, 0x17f, "Navitas" },
+ { 0x901, 0x18f, "Cellular @Sea" },
+ { 0x901, 0x19f, "Vodafone Malta Maritime" },
+ { 0x901, 0x21f, "Seanet" },
+ { 0x901, 0x24f, "iNum" },
+ { 0x901, 0x29f, "Telenor" },
+ { 0, 0, NULL }
+};
+
+/* GSM 03.22 Annex A */
+int gsm_match_mcc(uint16_t mcc, char *imsi)
+{
+ uint16_t sim_mcc;
+
+ sim_mcc = ((imsi[0] - '0') << 8)
+ + ((imsi[1] - '0') << 4)
+ + imsi[2] - '0';
+
+ return (mcc == sim_mcc);
+}
+
+/* GSM 03.22 Annex A */
+int gsm_match_mnc(uint16_t mcc, uint8_t mnc, char *imsi)
+{
+ uint16_t sim_mnc;
+
+ /* 1. SIM-MCC = BCCH-MCC */
+ if (!gsm_match_mcc(mcc, imsi))
+ return 0;
+
+ /* 2. 3rd digit of BCCH-MNC is not 0xf */
+ if ((mnc & 0x00f) != 0x00f) {
+ /* 3. 3 digit SIM-MNC = BCCH-MNC */
+ sim_mnc = ((imsi[3] - '0') << 8)
+ + ((imsi[4] - '0') << 4)
+ + imsi[5] - '0';
+
+ return (mnc == sim_mnc);
+ }
+
+ /* 4. BCCH-MCC in the range 310-316 */
+ if (mcc >= 310 && mcc <= 316) {
+ /* 5. 3rd diit of SIM-MNC is 0 */
+ if (imsi[5] != 0)
+ return 0;
+ }
+
+ /* 6. 1st 2 digits of SIM-MNC and BCCH-MNC match */
+ sim_mnc = ((imsi[3] - '0') << 8)
+ + ((imsi[4] - '0') << 4)
+ + 0x00f;
+
+ return (mnc == sim_mnc);
+}
+
+const char *gsm_print_mcc(uint16_t mcc)
+{
+ static char string[5] = "000";
+
+ snprintf(string, 4, "%03x", mcc);
+ return string;
+}
+
+const char *gsm_print_mnc(uint16_t mnc)
+{
+ static char string[7];
+
+ /* 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);
+ return string;
+ }
+
+ /* two digits */
+ if ((mnc & 0x000f) == 0x000f) {
+ snprintf(string, 6, "%02x", mnc >> 4);
+ return string;
+ }
+
+ /* three digits */
+ snprintf(string, 6, "%03x", mnc);
+ return string;
+}
+
+const uint16_t gsm_input_mcc(char *string)
+{
+ uint16_t mcc;
+
+ if (strlen(string) != 3)
+ return 0;
+ if (string[0] < '0' || string [0] > '9'
+ || string[1] < '0' || string [1] > '9'
+ || string[2] < '0' || string [2] > '9')
+ return 0;
+
+ mcc = ((string[0] - '0') << 8)
+ | ((string[1] - '0') << 4)
+ | ((string[2] - '0'));
+
+ return mcc;
+}
+
+const uint16_t gsm_input_mnc(char *string)
+{
+ uint16_t mnc = 0;
+
+ if (strlen(string) == 2) {
+ if (string[0] < '0' || string [0] > '9'
+ || string[1] < '0' || string [1] > '9')
+ return 0;
+
+ mnc = ((string[0] - '0') << 8)
+ | ((string[1] - '0') << 4)
+ | 0x00f;
+ } else
+ if (strlen(string) == 3) {
+ if (string[0] < '0' || string [0] > '9'
+ || string[1] < '0' || string [1] > '9'
+ || string[2] < '0' || string [2] > '9')
+ return 0;
+
+ mnc = ((string[0] - '0') << 8)
+ | ((string[1] - '0') << 4)
+ | ((string[2] - '0'));
+ }
+
+ return mnc;
+}
+
+const char *gsm_get_mcc(uint16_t mcc)
+{
+ int i;
+
+ for (i = 0; gsm_networks[i].name; i++)
+ if (gsm_networks[i].mnc < 0 && gsm_networks[i].mcc == mcc)
+ return gsm_networks[i].name;
+
+ return gsm_print_mcc(mcc);
+}
+
+const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc)
+{
+ int i;
+
+ for (i = 0; gsm_networks[i].name; i++)
+ if (gsm_networks[i].mcc == mcc && gsm_networks[i].mnc == mnc)
+ return gsm_networks[i].name;
+
+ return gsm_print_mnc(mnc);
+}
+
+/* get MCC from IMSI */
+const char *gsm_imsi_mcc(char *imsi)
+{
+ int i, found = 0;
+ uint16_t mcc;
+
+ mcc = ((imsi[0] - '0') << 8)
+ | ((imsi[1] - '0') << 4)
+ | ((imsi[2] - '0'));
+
+ for (i = 0; gsm_networks[i].name; i++) {
+ if (gsm_networks[i].mcc == mcc) {
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0)
+ return "Unknown";
+
+ return gsm_networks[i].name;
+}
+
+/* get MNC from IMSI */
+const char *gsm_imsi_mnc(char *imsi)
+{
+ int i, found = 0, position = 0;
+ uint16_t mcc, mnc2, mnc3;
+
+ mcc = ((imsi[0] - '0') << 8)
+ | ((imsi[1] - '0') << 4)
+ | ((imsi[2] - '0'));
+ mnc2 = ((imsi[3] - '0') << 8)
+ + ((imsi[4] - '0') << 4)
+ + 0x00f;
+ mnc3 = ((imsi[3] - '0') << 8)
+ + ((imsi[4] - '0') << 4)
+ + imsi[5] - '0';
+
+ for (i = 0; gsm_networks[i].name; i++) {
+ if (gsm_networks[i].mcc != mcc)
+ continue;
+ if ((gsm_networks[i].mnc & 0x00f) == 0x00f) {
+ if (mnc2 == gsm_networks[i].mnc) {
+ found++;
+ position = i;
+ }
+ } else {
+ if (mnc3 == gsm_networks[i].mnc) {
+ found++;
+ position = i;
+ }
+ }
+ }
+
+ if (found == 0)
+ return "Unknown";
+ if (found > 1)
+ return "Ambiguous";
+ return gsm_networks[position].name;
+}
+
+
diff --git a/src/host/layer23/src/rslms.c b/src/host/layer23/src/rslms.c
new file mode 100644
index 00000000..f8e15663
--- /dev/null
+++ b/src/host/layer23/src/rslms.c
@@ -0,0 +1,156 @@
+/* RSLms - GSM 08.58 like protocol between L2 and L3 of GSM Um interface */
+
+/* (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 <errno.h>
+#include <stdio.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/rsl.h>
+#include <osmocore/tlv.h>
+#include <osmocore/protocol/gsm_04_08.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/lapdm.h>
+#include <osmocom/rslms.h>
+#include <osmocom/layer3.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/l1ctl.h>
+
+/* Send a 'simple' RLL request to L2 */
+int rslms_tx_rll_req(struct osmocom_ms *ms, uint8_t msg_type,
+ uint8_t chan_nr, uint8_t link_id)
+{
+ struct msgb *msg;
+
+ msg = rsl_rll_simple(msg_type, chan_nr, link_id, 1);
+
+ return rslms_recvmsg(msg, ms);
+}
+
+/* Send a RLL request (including L3 info) to L2 */
+int rslms_tx_rll_req_l3(struct osmocom_ms *ms, uint8_t msg_type,
+ uint8_t chan_nr, uint8_t link_id, struct msgb *msg)
+{
+ rsl_rll_push_l3(msg, msg_type, chan_nr, link_id, 1);
+
+ return rslms_recvmsg(msg, ms);
+}
+
+static int ccch_enabled = 0;
+static int rach_count = 0;
+
+static int rslms_rx_udata_ind(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ struct tlv_parsed tv;
+ int rc = 0;
+
+ DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n",
+ rllh->chan_nr, rllh->link_id);
+
+ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
+ DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n");
+ return -EIO;
+ }
+ msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
+
+ if (rllh->chan_nr == RSL_CHAN_PCH_AGCH) {
+ rc = gsm48_rx_ccch(msg, ms);
+ ccch_enabled = 1;
+ } else if (rllh->chan_nr == RSL_CHAN_BCCH) {
+ rc = gsm48_rx_bcch(msg, ms);
+ if (ccch_enabled && (rach_count < 2)) {
+ tx_ph_rach_req(ms);
+ rach_count++;
+ }
+ }
+
+ return rc;
+}
+
+static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int rc = 0;
+
+ switch (rllh->c.msg_type) {
+ case RSL_MT_DATA_IND:
+ DEBUGP(DRSL, "RSLms DATA IND\n");
+ /* FIXME: implement this */
+ break;
+ case RSL_MT_UNIT_DATA_IND:
+ rc = rslms_rx_udata_ind(msg, ms);
+ break;
+ case RSL_MT_EST_IND:
+ DEBUGP(DRSL, "RSLms EST IND\n");
+ /* FIXME: implement this */
+ break;
+ case RSL_MT_EST_CONF:
+ DEBUGP(DRSL, "RSLms EST CONF\n");
+ /* FIXME: implement this */
+ break;
+ case RSL_MT_REL_CONF:
+ DEBUGP(DRSL, "RSLms REL CONF\n");
+ /* FIXME: implement this */
+ break;
+ case RSL_MT_ERROR_IND:
+ DEBUGP(DRSL, "RSLms ERR IND\n");
+ /* FIXME: implement this */
+ break;
+ default:
+ LOGP(DRSL, LOGL_NOTICE, "unknown RSLms message type "
+ "0x%02x\n", rllh->c.msg_type);
+ rc = -EINVAL;
+ break;
+ }
+ msgb_free(msg);
+ return rc;
+}
+
+/* input function that L2 calls when sending messages up to L3 */
+static int layer3_from_layer2(struct msgb *msg, struct osmocom_ms *ms)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = rslms_rx_rll(msg, ms);
+ break;
+ default:
+ /* FIXME: implement this */
+ LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n",
+ rslh->msg_discr);
+ msgb_free(msg);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+int layer3_init(struct osmocom_ms *ms)
+{
+ return osmol2_register_handler(ms, &layer3_from_layer2);
+}
diff --git a/src/host/layer23/src/settings.c b/src/host/layer23/src/settings.c
new file mode 100644
index 00000000..096b3db7
--- /dev/null
+++ b/src/host/layer23/src/settings.c
@@ -0,0 +1,87 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <osmocore/talloc.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/networks.h>
+
+int gsm_settings_init(struct osmocom_ms *ms)
+{
+ struct gsm_settings *set = &ms->settings;
+
+ /* IMEI */
+ sprintf(set->imei, "000000000000000");
+ sprintf(set->imeisv, "0000000000000000");
+
+ /* test sim */
+ strcpy(set->test_imsi, "001010000000000");
+ set->test_rplmn_mcc = set->test_rplmn_mnc = 1;
+
+ return 0;
+}
+
+char *gsm_check_imei(const char *imei, const char *sv)
+{
+ int i;
+
+ if (!imei || strlen(imei) != 15)
+ return "IMEI must have 15 digits!";
+
+ for (i = 0; i < strlen(imei); i++) {
+ if (imei[i] < '0' || imei[i] > '9')
+ return "IMEI must have digits 0 to 9 only!";
+ }
+
+ if (!sv || strlen(sv) != 1)
+ return "Software version must have 1 digit!";
+
+ if (sv[0] < '0' || sv[0] > '9')
+ return "Software version must have digits 0 to 9 only!";
+
+ return NULL;
+}
+
+int gsm_random_imei(struct gsm_settings *set)
+{
+ int digits = set->imei_random;
+ char rand[16];
+
+ if (digits <= 0)
+ return 0;
+ if (digits > 15)
+ digits = 15;
+
+ sprintf(rand, "%08ld", random() % 100000000);
+ sprintf(rand + 8, "%07ld", random() % 10000000);
+
+ strcpy(set->imei + 15 - digits, rand + 15 - digits);
+ strncpy(set->imeisv, set->imei, 15);
+
+ return 0;
+}
+
+
+
diff --git a/src/host/layer23/src/subscriber.c b/src/host/layer23/src/subscriber.c
new file mode 100644
index 00000000..80ea5a43
--- /dev/null
+++ b/src/host/layer23/src/subscriber.c
@@ -0,0 +1,319 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <osmocore/talloc.h>
+
+#include <osmocom/logging.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/networks.h>
+
+void *l23_ctx;
+
+int gsm_subscr_init(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+
+ memset(subscr, 0, sizeof(*subscr));
+ subscr->ms = ms;
+
+ /* set key invalid */
+ subscr->key_seq = 7;
+
+ /* init lists */
+ INIT_LLIST_HEAD(&subscr->plmn_list);
+ INIT_LLIST_HEAD(&subscr->plmn_na);
+
+ return 0;
+}
+
+int gsm_subscr_exit(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct llist_head *lh, *lh2;
+
+ /* flush lists */
+ llist_for_each_safe(lh, lh2, &subscr->plmn_list) {
+ llist_del(lh);
+ talloc_free(lh);
+ }
+ llist_for_each_safe(lh, lh2, &subscr->plmn_na) {
+ llist_del(lh);
+ talloc_free(lh);
+ }
+
+ return 0;
+}
+
+/* Attach test card, no sim must be present */
+int gsm_subscr_testcard(struct osmocom_ms *ms)
+{
+ struct gsm_settings *set = &ms->settings;
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *msg;
+ char *error;
+
+ if (subscr->sim_valid) {
+ LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card "
+ "is detached.\n");
+ return -EBUSY;
+ }
+
+ error = gsm_check_imsi(set->test_imsi);
+ if (error) {
+ LOGP(DMM, LOGL_ERROR, "%s\n", error);
+ return -EINVAL;
+ }
+
+ /* reset subscriber */
+ gsm_subscr_exit(ms);
+ gsm_subscr_init(ms);
+
+ sprintf(subscr->sim_name, "test");
+ // TODO: load / save SIM to file system
+ subscr->sim_valid = 1;
+ subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
+ subscr->acc_barr = set->test_barr; /* we may access barred cell */
+ subscr->acc_class = 0xffff; /* we have any access class */
+ subscr->plmn_valid = set->test_rplmn_valid;
+ subscr->plmn_mcc = set->test_rplmn_mcc;
+ subscr->plmn_mnc = set->test_rplmn_mnc;
+ subscr->always_search_hplmn = set->test_always;
+ subscr->t6m_hplmn = 1; /* try to find home network every 6 min */
+ strcpy(subscr->imsi, set->test_imsi);
+
+ LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s,%s)\n",
+ ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi),
+ gsm_imsi_mnc(subscr->imsi));
+
+ /* insert card */
+ msg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ);
+ if (!msg)
+ return -ENOMEM;
+ gsm48_mmr_downmsg(ms, msg);
+
+ return 0;
+}
+
+/* Detach card */
+int gsm_subscr_remove(struct osmocom_ms *ms)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct msgb *msg;
+
+ if (!subscr->sim_valid) {
+ LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n");
+ return -EINVAL;
+ }
+
+ /* remove card */
+ msg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ);
+ if (!msg)
+ return -ENOMEM;
+ gsm48_mmr_downmsg(ms, msg);
+
+ return 0;
+}
+
+static const char *subscr_ustate_names[] = {
+ "U0_NULL",
+ "U1_UPDATED",
+ "U2_NOT_UPDATED",
+ "U3_ROAMING_NA"
+};
+
+/* change to new U state */
+void new_sim_ustate(struct gsm_subscriber *subscr, int state)
+{
+ LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name,
+ subscr_ustate_names[subscr->ustate],
+ subscr_ustate_names[state]);
+
+ subscr->ustate = state;
+}
+
+/* del forbidden PLMN */
+int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
+ uint16_t mnc)
+{
+ struct gsm_sub_plmn_na *na;
+
+ llist_for_each_entry(na, &subscr->plmn_na, entry) {
+ if (na->mcc == mcc && na->mnc == mnc) {
+ LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
+ "PLMNs (mcc=%s, mnc=%s)\n",
+ gsm_print_mcc(mcc), gsm_print_mnc(mnc));
+ llist_del(&na->entry);
+ talloc_free(na);
+#ifdef TODO
+ update plmn not allowed list on sim
+#endif
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* add forbidden PLMN */
+int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
+ uint16_t mnc, uint8_t cause)
+{
+ struct gsm_sub_plmn_na *na;
+
+ /* don't add Home PLMN */
+ if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi))
+ return -EINVAL;
+
+ LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs "
+ "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc));
+ na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na);
+ if (!na)
+ return -ENOMEM;
+ na->mcc = mcc;
+ na->mnc = mnc;
+ na->cause = cause;
+ llist_add_tail(&na->entry, &subscr->plmn_na);
+
+#ifdef TODO
+ update plmn not allowed list on sim
+#endif
+
+ return 0;
+}
+
+/* search forbidden PLMN */
+int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
+ uint16_t mnc)
+{
+ struct gsm_sub_plmn_na *na;
+
+ llist_for_each_entry(na, &subscr->plmn_na, entry) {
+ if (na->mcc == mcc && na->mnc == mnc)
+ return 1;
+ }
+
+ return 0;
+}
+
+int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ struct gsm_subscriber *subscr = &ms->subscr;
+ struct gsm_sub_plmn_na *temp;
+
+ print(priv, "MCC |MNC |cause\n");
+ print(priv, "-------+-------+-------\n");
+ llist_for_each_entry(temp, &subscr->plmn_na, entry)
+ print(priv, "%s |%s%s |#%d\n",
+ gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
+ ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause);
+
+ return 0;
+}
+
+/* dump subscriber */
+void gsm_subscr_dump(struct gsm_subscriber *subscr,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ int i;
+ struct gsm_sub_plmn_list *plmn_list;
+ struct gsm_sub_plmn_na *plmn_na;
+
+ print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name);
+
+ if (!subscr->sim_valid) {
+ print(priv, " No SIM present.\n");
+ return;
+ }
+
+ print(priv, " IMSI: %s\n", subscr->imsi);
+ print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate],
+ (subscr->imsi_attached) ? "attached" : "detached");
+ if (subscr->tmsi_valid)
+ print(priv, " TSMI %08x", subscr->tmsi);
+ if (subscr->lai_valid)
+ print(priv, " LAI: MCC %s MNC %s LAC 0x%04x (%s, %s)\n",
+ gsm_print_mcc(subscr->lai_mcc),
+ gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac,
+ gsm_get_mcc(subscr->lai_mcc),
+ gsm_get_mnc(subscr->lai_mcc, subscr->lai_mnc));
+ else
+ print(priv, " LAI: invalid\n");
+ if (subscr->key_seq != 7) {
+ print(priv, " Key: sequence %d ");
+ for (i = 0; i < sizeof(subscr->key); i++)
+ print(priv, " %02x", subscr->key[i]);
+ print(priv, "\n");
+ }
+ if (subscr->plmn_valid)
+ print(priv, " Current PLMN: MCC %s MNC %s (%s, %s)\n",
+ gsm_print_mcc(subscr->plmn_mcc),
+ gsm_print_mnc(subscr->plmn_mnc),
+ gsm_get_mcc(subscr->plmn_mcc),
+ gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc));
+ print(priv, " Access barred cells: %s\n",
+ (subscr->acc_barr) ? "yes" : "no");
+ print(priv, " Access classes:");
+ for (i = 0; i < 16; i++)
+ if ((subscr->acc_class & (1 << i)))
+ print(priv, " C%d", i);
+ print(priv, "\n");
+ if (!llist_empty(&subscr->plmn_list)) {
+ print(priv, " List of preferred PLMNs:\n");
+ print(priv, " MCC |MNC\n");
+ print(priv, " -------+-------\n");
+ llist_for_each_entry(plmn_list, &subscr->plmn_list, entry)
+ print(priv, " %s |%s\n",
+ gsm_print_mcc(plmn_list->mcc),
+ gsm_print_mnc(plmn_list->mnc));
+ }
+ if (!llist_empty(&subscr->plmn_na)) {
+ print(priv, " List of forbidden PLMNs:\n");
+ print(priv, " MCC |MNC |cause\n");
+ print(priv, " -------+-------+-------\n");
+ llist_for_each_entry(plmn_na, &subscr->plmn_na, entry)
+ print(priv, " %s |%s%s |#%d\n",
+ gsm_print_mcc(plmn_na->mcc),
+ gsm_print_mnc(plmn_na->mnc),
+ ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"",
+ plmn_na->cause);
+ }
+}
+
+char *gsm_check_imsi(const char *imsi)
+{
+ int i;
+
+ if (!imsi || strlen(imsi) != 15)
+ return "IMSI must have 15 digits!";
+
+ for (i = 0; i < strlen(imsi); i++) {
+ if (imsi[i] < '0' || imsi[i] > '9')
+ return "IMSI must have digits 0 to 9 only!";
+ }
+
+ return NULL;
+}
+
+
diff --git a/src/host/layer23/src/support.c b/src/host/layer23/src/support.c
new file mode 100644
index 00000000..74476c07
--- /dev/null
+++ b/src/host/layer23/src/support.c
@@ -0,0 +1,155 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/osmocom_data.h>
+
+void gsm_support_init(struct osmocom_ms *ms)
+{
+ struct gsm_support *sup = &ms->support;
+ int i;
+
+ memset(sup, 0, sizeof(*sup));
+ sup->ms = ms;
+
+ /* rf power capability */
+ sup->pwr_lev_900 = 3; /* CLASS 4: Handheld 2W */
+ sup->pwr_lev_1800 = 0; /* CLASS 1: Handheld 1W */
+ /* controlled early classmark sending */
+ sup->es_ind = 0; /* no */
+ /* revision level */
+ sup->rev_lev = 1; /* phase 2 mobile station */
+ /* support of VGCS */
+ sup->vgcs = 0; /* no */
+ /* support of VBS */
+ sup->vbs = 0; /* no */
+ /* support of SMS */
+ sup->sms_ptp = 1; /* yes */
+ /* screening indicator */
+ sup->ss_ind = 1; /* phase 2 error handling */
+ /* pseudo synchronised capability */
+ sup->ps_cap = 0; /* no */
+ /* CM service prompt */
+ sup->cmsp = 0; /* no */
+ /* solsa support */
+ sup->solsa = 0; /* no */
+ /* location service support */
+ sup->lcsva = 0; /* no */
+ sup->loc_serv = 0; /* no */
+ /* codec supprot */
+ sup->a5_1 = 0; /* currently not */
+ sup->a5_2 = 0;
+ sup->a5_3 = 0;
+ sup->a5_4 = 0;
+ sup->a5_5 = 0;
+ sup->a5_6 = 0;
+ sup->a5_7 = 0;
+ /* radio support */
+ sup->p_gsm = 1; /* P-GSM only */
+ sup->e_gsm = 1; /* E-GSM */
+ sup->r_gsm = 1; /* R-GSM */
+ sup->r_capa = 0;
+ sup->low_capa = 4; /* p,e,r power class */
+ sup->dcs_1800 = 1;
+ /* set supported frequencies */
+ if (sup->e_gsm || sup->r_gsm)
+ sup->freq_map[0] |= 1;
+ if (sup->p_gsm || sup->e_gsm || sup->r_gsm)
+ for(i = 1; i <= 124; i++)
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ if (sup->dcs_1800)
+ for(i = 512; i <= 885; i++)
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ if (sup->e_gsm)
+ for(i = 975; i <= 1023; i++)
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+// for(i = 978; i <= 978; i++)
+// sup->freq_map[i >> 3] |= (1 << (i & 7));
+ if (sup->r_gsm)
+ for(i = 955; i <= 1023; i++)
+ sup->freq_map[i >> 3] |= (1 << (i & 7));
+ sup->dcs_capa = 1; /* dcs power class */
+ /* multi slot support */
+ sup->ms_sup = 0; /* no */
+ /* ucs2 treatment */
+ sup->ucs2_treat = 0; /* default */
+ /* support extended measurements */
+ sup->ext_meas = 0; /* no */
+ /* support switched measurement capability */
+ sup->meas_cap = 0; /* no */
+ //sup->sms_val = ;
+ //sup->sm_val = ;
+
+ /* radio */
+ sup->min_rxlev_db = -100; // TODO
+ sup->sync_to = 6; /* how long to wait sync (0.9 s) */
+ sup->scan_to = 4; /* how long to wait for all sysinfos (>=4 s) */
+}
+
+/* (3.2.1) maximum channels to scan within each band */
+struct gsm_support_scan_max gsm_sup_smax[] = {
+ { 259, 293, 15, 0 }, /* GSM 450 */
+ { 306, 340, 15, 0 }, /* GSM 480 */
+ { 438, 511, 25, 0 },
+ { 128, 251, 30, 0 },
+ { 955, 124, 30, 0 },
+ { 512, 885, 40, 0 }, /* DCS 1800 */
+ { 0, 0, 0, 0 }
+};
+
+/* dump support */
+void gsm_support_dump(struct gsm_support *sup,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ print(priv, "Supported features of MS '%s':\n", sup->ms->name);
+ if (sup->r_gsm)
+ print(priv, " R-GSM");
+ if (sup->e_gsm || sup->r_gsm)
+ print(priv, " E-GSM");
+ if (sup->p_gsm || sup->p_gsm || sup->r_gsm)
+ print(priv, " P-GSM");
+ if (sup->dcs_1800)
+ print(priv, " DCS1800");
+ print(priv, " (Phase %d mobile station)\n", sup->rev_lev + 1);
+ print(priv, " CECS : %s\n", (sup->es_ind) ? "yes" : "no");
+ print(priv, " VGCS : %s\n", (sup->vgcs) ? "yes" : "no");
+ print(priv, " VBS : %s\n", (sup->vbs) ? "yes" : "no");
+ print(priv, " SMS : %s\n", (sup->sms_ptp) ? "yes" : "no");
+ print(priv, " SS_IND : %s\n", (sup->ss_ind) ? "yes" : "no");
+ print(priv, " PS_CAP : %s\n", (sup->ps_cap) ? "yes" : "no");
+ print(priv, " CMSP : %s\n", (sup->cmsp) ? "yes" : "no");
+ print(priv, " SoLSA : %s\n", (sup->solsa) ? "yes" : "no");
+ print(priv, " LCSVA : %s\n", (sup->lcsva) ? "yes" : "no");
+ print(priv, " LOC_SERV : %s\n", (sup->loc_serv) ? "yes" : "no");
+ print(priv, " A5/1 : %s\n", (sup->a5_1) ? "yes" : "no");
+ print(priv, " A5/2 : %s\n", (sup->a5_2) ? "yes" : "no");
+ print(priv, " A5/3 : %s\n", (sup->a5_3) ? "yes" : "no");
+ print(priv, " A5/4 : %s\n", (sup->a5_4) ? "yes" : "no");
+ print(priv, " A5/5 : %s\n", (sup->a5_5) ? "yes" : "no");
+ print(priv, " A5/6 : %s\n", (sup->a5_6) ? "yes" : "no");
+ print(priv, " A5/7 : %s\n", (sup->a5_7) ? "yes" : "no");
+ print(priv, " A5/1 : %s\n", (sup->a5_1) ? "yes" : "no");
+ print(priv, " Min RXLEV: %d\n", sup->min_rxlev_db);
+}
+
diff --git a/src/host/layer23/src/sysinfo.c b/src/host/layer23/src/sysinfo.c
new file mode 100644
index 00000000..ca62eb68
--- /dev/null
+++ b/src/host/layer23/src/sysinfo.c
@@ -0,0 +1,178 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/osmocom_data.h>
+#include <osmocom/networks.h>
+
+int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn,
+ void (*print)(void *, const char *, ...), void *priv)
+{
+ char buffer[80];
+ int i, j;
+
+ /* available sysinfos */
+ print(priv, "ARFCN = %d\n", arfcn);
+ print(priv, "Available SYSTEM INFORMATIONS =");
+ if (s->si1)
+ print(priv, " 1");
+ if (s->si2)
+ print(priv, " 2");
+ if (s->si2bis)
+ print(priv, " 2bis");
+ if (s->si2ter)
+ print(priv, " 2ter");
+ if (s->si3)
+ print(priv, " 3");
+ if (s->si4)
+ print(priv, " 4");
+ if (s->si5)
+ print(priv, " 5");
+ if (s->si5bis)
+ print(priv, " 5bis");
+ if (s->si5ter)
+ print(priv, " 5ter");
+ if (s->si6)
+ print(priv, " 6");
+ print(priv, "\n");
+ print(priv, "\n");
+
+ /* frequency map */
+ for (i = 0; i < 1024; i += 64) {
+ if (i < 10)
+ sprintf(buffer, " %d ", i);
+ else if (i < 100)
+ sprintf(buffer, " %d ", i);
+ else
+ sprintf(buffer, " %d ", i);
+ for (j = 0; j < 64; j++) {
+ if ((s->freq[i+j].mask & FREQ_TYPE_SERV))
+ buffer[j + 5] = 'S';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_HOPP))
+ buffer[j + 5] = 'H';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL_2))
+ buffer[j + 5] = 'N';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL_2bis))
+ buffer[j + 5] = 'b';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL_2ter))
+ buffer[j + 5] = 't';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_REP_5))
+ buffer[j + 5] = 'R';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_REP_5bis))
+ buffer[j + 5] = 'b';
+ else if ((s->freq[i+j].mask & FREQ_TYPE_REP_5ter))
+ buffer[j + 5] = 't';
+ else
+ buffer[j + 5] = '.';
+ }
+ sprintf(buffer + 69, " %d", i + 63);
+ print(priv, "%s\n", buffer);
+ }
+ print(priv, " S = serv. cell H = hopping seq. N,b,t = neigh. cells "
+ "R,b,t = cells to rep.\n\n");
+
+ /* serving cell */
+ print(priv, "Serving Cell:\n");
+ print(priv, " MCC = %03d MNC = %02d LAC = 0x%04x Cell ID = 0x%04x "
+ "(%s, %s)\n", s->mcc, s->mnc, s->lac, s->cell_id,
+ gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc));
+ print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n",
+ s->max_retrans, s->tx_integer,
+ (s->reest_denied) ? "denied" : "allowed");
+ print(priv, " Cell barred = %s barred classes =",
+ (s->cell_barr ? "yes" : "no"));
+ for (i = 0; i < 16; i++) {
+ if ((s->class_barr & (1 << i)))
+ print(priv, " C%d", i);
+ }
+ print(priv, "\n");
+ if (s->sp)
+ print(priv, " CBQ = %d CRO = %d TEMP_OFFSET = %d "
+ "PENALTY_TIME = %d\n", s->sp_cbq, s->sp_cro, s->sp_to,
+ s->sp_pt);
+ print(priv, "\n");
+
+ /* neighbor cell */
+ print(priv, "Neighbor Cell:\n");
+ print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n",
+ s->nb_max_retrans, s->nb_tx_integer,
+ (s->nb_reest_denied) ? "denied" : "allowed");
+ print(priv, " Cell barred = %s barred classes =",
+ (s->nb_cell_barr ? "yes" : "no"));
+ for (i = 0; i < 16; i++) {
+ if ((s->nb_class_barr & (1 << i)))
+ print(priv, " C%d", i);
+ }
+ print(priv, "\n");
+ print(priv, "\n");
+
+ /* cell selection */
+ print(priv, "MX_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d "
+ "NECI = %d ACS = %d\n", s->ms_txpwr_max_ccch,
+ s->cell_resel_hyst_db, s->rxlev_acc_min_db, s->neci, s->acs);
+
+ /* bcch options */
+ print(priv, "BCCH link timeout = %d DTX = %d PWRC = %d\n",
+ s->bcch_radio_link_timeout, s->bcch_dtx, s->bcch_pwrc);
+
+ /* sacch options */
+ print(priv, "SACCH link timeout = %d DTX = %d PWRC = %d\n",
+ s->sacch_radio_link_timeout, s->sacch_dtx, s->sacch_pwrc);
+
+ /* control channel */
+ switch(s->ccch_conf) {
+ case 0:
+ print(priv, "CCCH Config = 1 CCCH");
+ break;
+ case 1:
+ print(priv, "CCCH Config = 1 CCCH + SDCCH");
+ break;
+ case 2:
+ print(priv, "CCCH Config = 2 CCCH");
+ break;
+ case 4:
+ print(priv, "CCCH Config = 3 CCCH");
+ break;
+ case 6:
+ print(priv, "CCCH Config = 4 CCCH");
+ break;
+ default:
+ print(priv, "CCCH Config = reserved");
+ }
+ print(priv, " BS-PA-MFMS = %d Attachment = %s\n",
+ s->pag_mf_periods, (s->att_allowed) ? "allowed" : "denied");
+ print(priv, "BS-AG_BLKS_RES = %d\n", s->bs_ag_blks_res);
+
+ /* channel description */
+ if (s->h)
+ print(priv, "chan_nr = 0x%02x TSC = %d MAIO = %d HSN = %d\n",
+ s->chan_nr, s->tsc, s->maio, s->hsn);
+ else
+ print(priv, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n",
+ s->chan_nr, s->tsc, s->arfcn);
+ print(priv, "\n");
+
+ return 0;
+}
+
diff --git a/src/host/layer23/src/telnet_interface.c b/src/host/layer23/src/telnet_interface.c
new file mode 100644
index 00000000..c7b06f72
--- /dev/null
+++ b/src/host/layer23/src/telnet_interface.c
@@ -0,0 +1,240 @@
+/* minimalistic telnet/ms interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/telnet_interface.h>
+#include <osmocore/talloc.h>
+
+#include <vty/buffer.h>
+
+#define WRITE_CONNECTION(fd, msg...) \
+ int ret; \
+ char buf[4096]; \
+ snprintf(buf, sizeof(buf), msg); \
+ ret = write(fd, buf, strlen(buf));
+
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+void *l23_ctx;
+static void *tall_telnet_ctx;
+
+/* per ms data */
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
+
+static struct bsc_fd server_socket = {
+ .when = BSC_FD_READ,
+ .cb = telnet_new_connection,
+ .priv_nr = 0,
+};
+
+int telnet_init(struct osmocom_ms *ms, int port) {
+ struct sockaddr_in sock_addr;
+ int fd, on = 1;
+
+ tall_telnet_ctx = talloc_named_const(l23_ctx, 1,
+ "telnet_connection");
+
+ ms_vty_init(ms);
+
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ fprintf(stderr, "Telnet interface socket creation failed\n");
+ return -1;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(port);
+ sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
+ fprintf(stderr, "Telnet interface failed to bind\n");
+ return -1;
+ }
+
+ if (listen(fd, 0) < 0) {
+ fprintf(stderr, "Telnet interface failed to listen\n");
+ return -1;
+ }
+
+ server_socket.data = ms;
+ server_socket.fd = fd;
+ bsc_register_fd(&server_socket);
+
+ return 0;
+}
+
+static void print_welcome(int fd) {
+ int ret;
+ static char *msg =
+ "Welcome to the Osmocom Control interface\n"
+ "License GPLv2+: GNU GPL version 2 or later "
+ "<http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change "
+ "and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted "
+ "by law.\nType \"help\" to get a short introduction.\n";
+
+ ret = write(fd, msg, strlen(msg));
+}
+
+int telnet_close_client(struct bsc_fd *fd) {
+ struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+ close(fd->fd);
+ bsc_unregister_fd(fd);
+
+ if (conn->dbg) {
+// debug_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ }
+
+ llist_del(&conn->entry);
+ talloc_free(conn);
+ return 0;
+}
+
+static int client_data(struct bsc_fd *fd, unsigned int what)
+{
+ struct telnet_connection *conn = fd->data;
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ conn->fd.when &= ~BSC_FD_READ;
+ rc = vty_read(conn->vty);
+ }
+
+ /* vty might have been closed from vithin vty_read() */
+ if (!conn->vty)
+ return rc;
+
+ if (what & BSC_FD_WRITE) {
+ rc = buffer_flush_all(conn->vty->obuf, fd->fd);
+ if (rc == BUFFER_EMPTY)
+ conn->fd.when &= ~BSC_FD_WRITE;
+ }
+
+ return rc;
+}
+
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
+ struct telnet_connection *connection;
+ struct sockaddr_in sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+
+ if (new_connection < 0) {
+ fprintf(stderr, "telnet accept failed\n");
+ return -1;
+ }
+
+
+ connection = talloc_zero(tall_telnet_ctx, struct telnet_connection);
+ connection->ms = (struct osmocom_ms*)fd->data;
+ connection->fd.data = connection;
+ connection->fd.fd = new_connection;
+ connection->fd.when = BSC_FD_READ;
+ connection->fd.cb = client_data;
+ bsc_register_fd(&connection->fd);
+ llist_add_tail(&connection->entry, &active_connections);
+
+ print_welcome(new_connection);
+
+ connection->vty = vty_create(new_connection, connection);
+ if (!connection->vty) {
+ fprintf(stderr, "couldn't create VTY\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* callback from VTY code */
+void vty_event(enum event event, int sock, struct vty *vty)
+{
+ struct telnet_connection *connection = vty->priv;
+ struct bsc_fd *bfd = &connection->fd;
+
+ if (vty->type != VTY_TERM)
+ return;
+
+ switch (event) {
+ case VTY_READ:
+ bfd->when |= BSC_FD_READ;
+ break;
+ case VTY_WRITE:
+ bfd->when |= BSC_FD_WRITE;
+ break;
+ case VTY_CLOSED:
+ /* vty layer is about to free() vty */
+ connection->vty = NULL;
+ telnet_close_client(bfd);
+ break;
+ default:
+ break;
+ }
+}
+
+void vty_notify(struct osmocom_ms *ms, const char *fmt, ...)
+{
+ struct telnet_connection *connection;
+ char buffer[1000];
+ va_list args;
+ struct vty *vty;
+
+ if (fmt) {
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ if (!buffer[0])
+ return;
+ }
+
+ llist_for_each_entry(connection, &active_connections, entry) {
+ vty = connection->vty;
+ if (!vty)
+ continue;
+ if (!fmt) {
+ vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name,
+ VTY_NEWLINE);
+ continue;
+ }
+ if (buffer[strlen(buffer) - 1] == '\n') {
+ buffer[strlen(buffer) - 1] = '\0';
+ vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE);
+ buffer[strlen(buffer)] = '\n';
+ } else
+ vty_out(vty, "%% %s", buffer);
+ }
+}
+
diff --git a/src/host/layer23/src/transaction.c b/src/host/layer23/src/transaction.c
new file mode 100644
index 00000000..fbce57b6
--- /dev/null
+++ b/src/host/layer23/src/transaction.c
@@ -0,0 +1,143 @@
+/* GSM 04.07 Transaction handling */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/msgb.h>
+
+#include <osmocom/osmocom_data.h>
+#include <osmocom/mncc.h>
+#include <osmocom/transaction.h>
+#include <osmocom/logging.h>
+
+extern void *l23_ctx;
+
+void _gsm48_cc_trans_free(struct gsm_trans *trans);
+
+struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms,
+ uint8_t proto, uint8_t trans_id)
+{
+ struct gsm_trans *trans;
+
+ llist_for_each_entry(trans, &ms->trans_list, entry) {
+ if (trans->protocol == proto &&
+ trans->transaction_id == trans_id)
+ return trans;
+ }
+ return NULL;
+}
+
+struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms,
+ uint32_t callref)
+{
+ struct gsm_trans *trans;
+
+ llist_for_each_entry(trans, &ms->trans_list, entry) {
+ if (trans->callref == callref)
+ return trans;
+ }
+ return NULL;
+}
+
+struct gsm_trans *trans_alloc(struct osmocom_ms *ms,
+ uint8_t protocol, uint8_t trans_id,
+ uint32_t callref)
+{
+ struct gsm_trans *trans;
+
+ trans = talloc_zero(l23_ctx, struct gsm_trans);
+ if (!trans)
+ return NULL;
+
+ DEBUGP(DCC, "ms %s allocetes transaction (proto %d trans_id %d "
+ "callref %d mem %p)\n", ms->name, protocol, trans_id, callref,
+ trans);
+
+ trans->ms = ms;
+
+ trans->protocol = protocol;
+ trans->transaction_id = trans_id;
+ trans->callref = callref;
+
+ llist_add_tail(&trans->entry, &ms->trans_list);
+
+ return trans;
+}
+
+void trans_free(struct gsm_trans *trans)
+{
+ switch (trans->protocol) {
+ case GSM48_PDISC_CC:
+ _gsm48_cc_trans_free(trans);
+ break;
+#if 0
+ case GSM48_PDISC_SS:
+ _gsm411_ss_trans_free(trans);
+ break;
+ case GSM48_PDISC_SMS:
+ _gsm411_sms_trans_free(trans);
+ break;
+#endif
+ }
+
+ DEBUGP(DCC, "ms %s frees transaction (mem %p)\n", trans->ms->name,
+ trans);
+
+ llist_del(&trans->entry);
+
+ talloc_free(trans);
+}
+
+/* allocate an unused transaction ID
+ * in the given protocol using the ti_flag specified */
+int trans_assign_trans_id(struct osmocom_ms *ms,
+ uint8_t protocol, uint8_t ti_flag)
+{
+ struct gsm_trans *trans;
+ unsigned int used_tid_bitmask = 0;
+ int i, j, h;
+
+ if (ti_flag)
+ ti_flag = 0x8;
+
+ /* generate bitmask of already-used TIDs for this (proto) */
+ llist_for_each_entry(trans, &ms->trans_list, entry) {
+ if (trans->protocol != protocol ||
+ trans->transaction_id == 0xff)
+ continue;
+ used_tid_bitmask |= (1 << trans->transaction_id);
+ }
+
+ /* find a new one, trying to go in a 'circular' pattern */
+ for (h = 6; h > 0; h--)
+ if (used_tid_bitmask & (1 << (h | ti_flag)))
+ break;
+ for (i = 0; i < 7; i++) {
+ j = ((h + i) % 7) | ti_flag;
+ if ((used_tid_bitmask & (1 << j)) == 0)
+ return j;
+ }
+
+ return -1;
+}
+
diff --git a/src/host/layer23/src/vty_interface.c b/src/host/layer23/src/vty_interface.c
new file mode 100644
index 00000000..7d2a7111
--- /dev/null
+++ b/src/host/layer23/src/vty_interface.c
@@ -0,0 +1,997 @@
+/*
+ * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <osmocom/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+
+#include <osmocore/gsm48.h>
+#include <osmocom/osmocom_data.h>
+#include <osmocom/networks.h>
+#include <osmocom/mncc.h>
+#include <osmocom/transaction.h>
+
+int mncc_call(struct osmocom_ms *ms, char *number);
+int mncc_hangup(struct osmocom_ms *ms);
+int mncc_answer(struct osmocom_ms *ms);
+int mncc_hold(struct osmocom_ms *ms);
+int mncc_retrieve(struct osmocom_ms *ms, int number);
+
+extern struct llist_head ms_list;
+extern struct llist_head active_connections;
+
+struct cmd_node ms_node = {
+ MS_NODE,
+ "%s(ms)#",
+ 1
+};
+
+struct cmd_node testsim_node = {
+ TESTSIM_NODE,
+ "%s(test-sim)#",
+ 1
+};
+
+static void print_vty(void *priv, const char *fmt, ...)
+{
+ char buffer[1000];
+ struct vty *vty = priv;
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ if (buffer[0]) {
+ if (buffer[strlen(buffer) - 1] == '\n') {
+ buffer[strlen(buffer) - 1] = '\0';
+ vty_out(vty, "%s%s", buffer, VTY_NEWLINE);
+ } else
+ vty_out(vty, "%s", buffer);
+ }
+}
+
+static struct osmocom_ms *get_ms(const char *name, struct vty *vty)
+{
+ struct osmocom_ms *ms;
+
+ llist_for_each_entry(ms, &ms_list, entity) {
+ if (!strcmp(ms->name, name))
+ return ms;
+ }
+ vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE);
+
+ return NULL;
+}
+
+DEFUN(show_ms, show_ms_cmd, "show ms",
+ SHOW_STR "Display available MS entities\n")
+{
+ struct osmocom_ms *ms;
+
+ llist_for_each_entry(ms, &ms_list, entity) {
+ struct gsm_settings *set = &ms->settings;
+
+ vty_out(vty, "MS NAME: %s%s", ms->name, VTY_NEWLINE);
+ vty_out(vty, " IMEI: %s%s", set->imei, VTY_NEWLINE);
+ vty_out(vty, " IMEISV: %s%s", set->imeisv, VTY_NEWLINE);
+ if (set->imei_random)
+ vty_out(vty, " IMEI generation: random (%d trailing "
+ "digits)%s", set->imei_random, VTY_NEWLINE);
+ else
+ vty_out(vty, " IMEI generation: fixed%s", VTY_NEWLINE);
+ vty_out(vty, " network selection mode: %s%s",
+ (set->plmn_mode == PLMN_MODE_AUTO)
+ ? "automatic" : "manual", VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_support, show_support_cmd, "show support [ms_name]",
+ SHOW_STR "Display information about MS support\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ if (argc) {
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ gsm_support_dump(&ms->support, print_vty, vty);
+ } else {
+ llist_for_each_entry(ms, &ms_list, entity) {
+ gsm_support_dump(&ms->support, print_vty, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void gsm_states_dump(struct osmocom_ms *ms, struct vty *vty)
+{
+ struct gsm_trans *trans;
+
+ vty_out(vty, "Current state of MS '%s'%s", ms->name, VTY_NEWLINE);
+ if (ms->settings.plmn_mode == PLMN_MODE_AUTO)
+ vty_out(vty, " automatic network selection: %s%s",
+ plmn_a_state_names[ms->plmn.state], VTY_NEWLINE);
+ else
+ vty_out(vty, " manual network selection: %s%s",
+ plmn_m_state_names[ms->plmn.state], VTY_NEWLINE);
+ vty_out(vty, " cell selection: %s%s",
+ cs_state_names[ms->cellsel.state], VTY_NEWLINE);
+ vty_out(vty, " radio ressource layer: %s%s",
+ gsm48_rr_state_names[ms->rrlayer.state], VTY_NEWLINE);
+ vty_out(vty, " mobility management layer: %s",
+ gsm48_mm_state_names[ms->mmlayer.state]);
+ if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE)
+ vty_out(vty, ", %s",
+ gsm48_mm_substate_names[ms->mmlayer.substate]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ llist_for_each_entry(trans, &ms->trans_list, entry) {
+ vty_out(vty, " call control: %s%s",
+ gsm48_cc_state_name(trans->cc.state), VTY_NEWLINE);
+ }
+}
+
+DEFUN(show_states, show_states_cmd, "show states [ms_name]",
+ SHOW_STR "Display current states of given MS\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ if (argc) {
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ gsm_states_dump(ms, vty);
+ } else {
+ llist_for_each_entry(ms, &ms_list, entity) {
+ gsm_states_dump(ms, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_subscr, show_subscr_cmd, "show subscriber [ms_name]",
+ SHOW_STR "Display information about subscriber\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ if (argc) {
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ gsm_subscr_dump(&ms->subscr, print_vty, vty);
+ } else {
+ llist_for_each_entry(ms, &ms_list, entity) {
+ gsm_subscr_dump(&ms->subscr, print_vty, vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_cell, show_cell_cmd, "show cell MS_NAME",
+ SHOW_STR "Display information about received cells\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SYSINFO, print_vty,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023>",
+ SHOW_STR "Display information about received cell\n"
+ "Name of MS (see \"show ms\")\nRadio frequency number")
+{
+ struct osmocom_ms *ms;
+ int i;
+ struct gsm48_sysinfo *s;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ i = atoi(argv[1]);
+ if (i < 0 || i > 1023) {
+ vty_out(vty, "Given ARFCN '%s' not in range (0..1023)%s",
+ argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ s = ms->cellsel.list[i].sysinfo;
+ if (!s) {
+ vty_out(vty, "Given ARFCN '%s' has no sysinfo available%s",
+ argv[1], VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ gsm48_sysinfo_dump(s, i, print_vty, vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ba, show_ba_cmd, "show ba MS_NAME [mcc] [mnc]",
+ SHOW_STR "Display information about band allocations\n"
+ "Name of MS (see \"show ms\")\nMobile Country Code\n"
+ "Mobile Network Code")
+{
+ struct osmocom_ms *ms;
+ uint16_t mcc = 0, mnc = 0;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (argc >= 3) {
+ mcc = gsm_input_mcc((char *)argv[1]);
+ mnc = gsm_input_mnc((char *)argv[2]);
+ if (!mcc) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!mnc) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ gsm322_dump_ba_list(&ms->cellsel, mcc, mnc, print_vty, vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_forb_plmn, show_forb_plmn_cmd, "show forbidden plmn MS_NAME",
+ SHOW_STR "Display information about forbidden cells / networks\n"
+ "Display forbidden PLMNs\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ gsm_subscr_dump_forbidden_plmn(ms, print_vty, vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_forb_la, show_forb_la_cmd, "show forbidden location-area MS_NAME",
+ SHOW_STR "Display information about forbidden cells / networks\n"
+ "Display forbidden location areas\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ gsm322_dump_forbidden_la(ms, print_vty, vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(insert_test, insert_test_cmd, "insert testcard MS_NAME [mcc] [mnc]",
+ "Insert ...\nInsert test card\nName of MS (see \"show ms\")\n"
+ "Mobile Country Code\nMobile Network Code")
+{
+ struct osmocom_ms *ms;
+ uint16_t mcc = 1, mnc = 1;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (ms->subscr.sim_valid) {
+ vty_out(vty, "Sim already presend, remove first!%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (argc >= 3) {
+ mcc = gsm_input_mcc((char *)argv[1]);
+ mnc = gsm_input_mnc((char *)argv[2]);
+ if (!mcc) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!mnc) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ gsm_subscr_testcard(ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(remove_sim, remove_sim_cmd, "remove sim MS_NAME",
+ "Remove ...\nRemove SIM card\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ if (!ms->subscr.sim_valid) {
+ vty_out(vty, "No Sim inserted!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gsm_subscr_remove(ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC",
+ "Select ...\nSelect Network\nName of MS (see \"show ms\")\n"
+ "Mobile Country Code\nMobile Network Code")
+{
+ struct osmocom_ms *ms;
+ struct gsm322_plmn *plmn;
+ struct msgb *nmsg;
+ struct gsm322_msg *ngm;
+ struct gsm322_plmn_list *temp;
+ uint16_t mcc = gsm_input_mcc((char *)argv[1]),
+ mnc = gsm_input_mnc((char *)argv[2]);
+ int found = 0;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ plmn = &ms->plmn;
+
+ if (!mcc) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!mnc) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
+ if (temp->mcc == mcc && temp->mnc == mnc)
+ found = 1;
+ if (!found) {
+ vty_out(vty, "Network not in list!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOSE_PLMN);
+ if (!nmsg)
+ return CMD_WARNING;
+ ngm = (struct gsm322_msg *) nmsg->data;
+ ngm->mcc = mcc;
+ ngm->mnc = mnc;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)",
+ "Make a call\nName of MS (see \"show ms\")\nPhone number to call\n"
+ "Make an emergency call\nAnswer an incomming call\nHangup a call\n"
+ "Hold current active call\n")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ switch (argv[1][0]) {
+ case 'a':
+ mncc_answer(ms);
+ break;
+ case 'h':
+ if (argv[1][1] == 'a')
+ mncc_hangup(ms);
+ else
+ mncc_hold(ms);
+ break;
+ default:
+ mncc_call(ms, (char *)argv[1]);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [number]",
+ "Make a call\nName of MS (see \"show ms\")\n"
+ "Retrieve call on hold\nNumber of call to retrieve")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(network_show, network_show_cmd, "network show MS_NAME",
+ "Network ...\nShow results of network search (again)\n"
+ "Name of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+ struct gsm322_plmn *plmn;
+ struct gsm322_plmn_list *temp;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ plmn = &ms->plmn;
+
+ if (ms->settings.plmn_mode != PLMN_MODE_AUTO
+ && plmn->state != GSM322_M3_NOT_ON_PLMN) {
+ vty_out(vty, "Start network search first!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(temp, &plmn->sorted_plmn, entry)
+ vty_out(vty, " Network %s, %s (%s, %s)%s",
+ gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
+ gsm_get_mcc(temp->mcc),
+ gsm_get_mnc(temp->mcc, temp->mnc), VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(network_search, network_search_cmd, "network search MS_NAME",
+ "Network ...\nTrigger network search\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+ struct msgb *nmsg;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_USER_RESEL);
+ if (!nmsg)
+ return CMD_WARNING;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return CMD_SUCCESS;
+}
+
+/* per MS config */
+DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME",
+ "Select a mobile station to configure\nName of MS (see \"show ms\")")
+{
+ struct osmocom_ms *ms;
+
+ ms = get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+
+ vty->index = ms;
+ vty->node = MS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+static void config_write_ms_single(struct vty *vty, struct osmocom_ms *ms)
+{
+ struct gsm_settings *set = &ms->settings;
+
+ vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE);
+ switch(ms->settings.simtype) {
+ case GSM_SIM_TYPE_NONE:
+ vty_out(vty, " sim none%s", VTY_NEWLINE);
+ break;
+ case GSM_SIM_TYPE_SLOT:
+ vty_out(vty, " sim slot%s", VTY_NEWLINE);
+ break;
+ case GSM_SIM_TYPE_TEST:
+ vty_out(vty, " sim test%s", VTY_NEWLINE);
+ break;
+ }
+ vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode
+ == PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE);
+ vty_out(vty, " imei %s %s%s", set->imei,
+ set->imeisv + strlen(set->imei), VTY_NEWLINE);
+ if (set->imei_random)
+ vty_out(vty, " imei-random %d%s", set->imei_random,
+ VTY_NEWLINE);
+ else
+ vty_out(vty, " imei-fixed%s", VTY_NEWLINE);
+ vty_out(vty, " emergency-imsi %s%s", (ms->settings.emergency_imsi[0]) ?
+ ms->settings.emergency_imsi : "none", VTY_NEWLINE);
+ vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ", VTY_NEWLINE);
+ vty_out(vty, " test-sim%s", VTY_NEWLINE);
+ vty_out(vty, " imsi %s%s", ms->settings.test_imsi, VTY_NEWLINE);
+ vty_out(vty, " %sbarred-access%s", (set->test_barr) ? "" : "no ",
+ VTY_NEWLINE);
+ if (ms->settings.test_rplmn_valid)
+ vty_out(vty, " rplmn %s %s%s",
+ gsm_print_mcc(ms->settings.test_rplmn_mcc),
+ gsm_print_mnc(ms->settings.test_rplmn_mnc),
+ VTY_NEWLINE);
+ else
+ vty_out(vty, " no rplmn%s", VTY_NEWLINE);
+ vty_out(vty, " hplmn-search %s%s", (set->test_always) ? "everywhere"
+ : "foreign-country", VTY_NEWLINE);
+ vty_out(vty, " exit%s", VTY_NEWLINE);
+ vty_out(vty, "exit%s", VTY_NEWLINE);
+ vty_out(vty, "!%s", VTY_NEWLINE);
+}
+
+static int config_write_ms(struct vty *vty)
+{
+ struct osmocom_ms *ms;
+
+ llist_for_each_entry(ms, &ms_list, entity)
+ config_write_ms_single(vty, ms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)",
+ "Set network selection mode\nAutomatic network selection\n"
+ "Manual network selection")
+{
+ struct osmocom_ms *ms = vty->index;
+ struct msgb *nmsg;
+
+ if (!ms->plmn.state) {
+ if (argv[0][0] == 'a')
+ ms->settings.plmn_mode = PLMN_MODE_AUTO;
+ else
+ ms->settings.plmn_mode = PLMN_MODE_MANUAL;
+
+ return CMD_SUCCESS;
+ }
+ if (argv[0][0] == 'a')
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_AUTO);
+ else
+ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_MANUAL);
+ if (!nmsg)
+ return CMD_WARNING;
+ gsm322_plmn_sendmsg(ms, nmsg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]",
+ "Set IMEI (enter without control digit)\n15 Digits IMEI\n"
+ "Software version digit")
+{
+ struct osmocom_ms *ms = vty->index;
+ char *error, *sv = "0";
+
+ if (argc >= 2)
+ sv = (char *)argv[1];
+
+ error = gsm_check_imei(argv[0], sv);
+ if (error) {
+ vty_out(vty, "%s%s", error, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ strcpy(ms->settings.imei, argv[0]);
+ strcpy(ms->settings.imeisv, argv[0]);
+ strcpy(ms->settings.imeisv + 15, sv);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed",
+ "Use fixed IMEI on every power on")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.imei_random = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>",
+ "Use random IMEI on every power on\n"
+ "Number of trailing digits to randomize")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.imei_random = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi (none|IMSI)",
+ "Use IMSI for emergency calls\n"
+ "Use IMSI of SIM or IMEI for emergency calls\n15 digits IMSI")
+{
+ struct osmocom_ms *ms = vty->index;
+ char *error;
+
+ if (argv[0][0] == 'n') {
+ ms->settings.emergency_imsi[0] = '\0';
+ return CMD_SUCCESS;
+ }
+
+ error = gsm_check_imsi(argv[0]);
+ if (error) {
+ vty_out(vty, "%s%s", error, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ strcpy(ms->settings.emergency_imsi, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_cw, cfg_ms_no_cw_cmd, "no call-waiting",
+ NO_STR "Disallow waiting calls")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.cw = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_cw, cfg_ms_cw_cmd, "call-waiting",
+ "Allow waiting calls")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.cw = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_clip, cfg_ms_clip_cmd, "clip",
+ "Force caller ID presentation")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.clip = 1;
+ ms->settings.clir = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_clir, cfg_ms_clir_cmd, "clir",
+ "Force caller ID restriction")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.clip = 0;
+ ms->settings.clir = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_clip, cfg_ms_no_clip_cmd, "no clip",
+ NO_STR "Disable forcing of caller ID presentation")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.clip = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_clir, cfg_ms_no_clir_cmd, "no clir",
+ NO_STR "Disable forcing of caller ID restriction")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.clir = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|test)",
+ "Set sim card type when powering on\nNo sim interted\n"
+ "Test sim inserted")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ switch (argv[0][0]) {
+ case 'n':
+ ms->settings.simtype = GSM_SIM_TYPE_NONE;
+ break;
+ case 's':
+ ms->settings.simtype = GSM_SIM_TYPE_SLOT;
+ break;
+ case 't':
+ ms->settings.simtype = GSM_SIM_TYPE_TEST;
+ break;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* per MS config */
+DEFUN(cfg_testsim, cfg_testsim_cmd, "test-sim",
+ "Configure test SIM emulation")
+{
+ vty->node = TESTSIM_NODE;
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_dummy(struct vty *vty)
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI",
+ "Set IMSI on test card\n15 digits IMSI")
+{
+ struct osmocom_ms *ms = vty->index;
+ char *error = gsm_check_imsi(argv[0]);
+
+ if (error) {
+ vty_out(vty, "%s%s", error, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ strcpy(ms->settings.test_imsi, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access",
+ "Allow access to barred cells")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.test_barr = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_no_barr, cfg_test_no_barr_cmd, "no barred-access",
+ NO_STR "Deny access to barred cells")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.test_barr = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn",
+ NO_STR "Unset Registered PLMN")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ ms->settings.test_rplmn_valid = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC",
+ "Set Registered PLMN\nMobile Country Code\nMobile Network Code")
+{
+ struct osmocom_ms *ms = vty->index;
+ uint16_t mcc = gsm_input_mcc((char *)argv[0]),
+ mnc = gsm_input_mnc((char *)argv[1]);
+
+ if (!mcc) {
+ vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (!mnc) {
+ vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ms->settings.test_rplmn_valid = 1;
+ ms->settings.test_rplmn_mcc = mcc;
+ ms->settings.test_rplmn_mnc = mnc;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-country)",
+ "Set Home PLMN search mode\n"
+ "Search for HPLMN when on any other network\n"
+ "Search for HPLMN when in a different country")
+{
+ struct osmocom_ms *ms = vty->index;
+
+ switch (argv[0][0]) {
+ case 'e':
+ ms->settings.test_always = 1;
+ break;
+ case 'f':
+ ms->settings.test_always = 0;
+ break;
+ }
+
+ return CMD_SUCCESS;
+}
+
+enum node_type ms_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case MS_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ break;
+ case TESTSIM_NODE:
+ vty->node = MS_NODE;
+ break;
+ default:
+ vty->node = CONFIG_NODE;
+ }
+
+ return vty->node;
+}
+
+/* Down vty node level. */
+gDEFUN(ournode_exit,
+ ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
+{
+ switch (vty->node) {
+ case MS_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ break;
+ case TESTSIM_NODE:
+ vty->node = MS_NODE;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* End of configuration. */
+gDEFUN(ournode_end,
+ ournode_end_cmd, "end", "End current mode and change to enable mode.")
+{
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case VTY_NODE:
+ case MS_NODE:
+ case TESTSIM_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+int ms_vty_init(void)
+{
+ install_element_ve(&show_ms_cmd);
+ install_element_ve(&show_subscr_cmd);
+ install_element_ve(&show_support_cmd);
+ install_element_ve(&show_states_cmd);
+ install_element_ve(&show_cell_cmd);
+ install_element_ve(&show_cell_si_cmd);
+ install_element_ve(&show_ba_cmd);
+ install_element_ve(&show_forb_la_cmd);
+ install_element_ve(&show_forb_plmn_cmd);
+
+ install_element(ENABLE_NODE, &insert_test_cmd);
+ install_element(ENABLE_NODE, &remove_sim_cmd);
+ install_element(ENABLE_NODE, &network_search_cmd);
+ install_element(ENABLE_NODE, &network_show_cmd);
+ install_element(ENABLE_NODE, &network_select_cmd);
+ install_element(ENABLE_NODE, &call_cmd);
+ install_element(ENABLE_NODE, &call_retr_cmd);
+
+ install_element(CONFIG_NODE, &cfg_ms_cmd);
+ install_element(CONFIG_NODE, &ournode_end_cmd);
+ install_node(&ms_node, config_write_ms);
+ install_default(MS_NODE);
+ install_element(MS_NODE, &ournode_exit_cmd);
+ install_element(MS_NODE, &ournode_end_cmd);
+ install_element(MS_NODE, &cfg_ms_mode_cmd);
+ install_element(MS_NODE, &cfg_ms_imei_cmd);
+ install_element(MS_NODE, &cfg_ms_imei_fixed_cmd);
+ install_element(MS_NODE, &cfg_ms_imei_random_cmd);
+ install_element(MS_NODE, &cfg_ms_emerg_imsi_cmd);
+ install_element(MS_NODE, &cfg_ms_cw_cmd);
+ install_element(MS_NODE, &cfg_ms_no_cw_cmd);
+ install_element(MS_NODE, &cfg_ms_clip_cmd);
+ install_element(MS_NODE, &cfg_ms_clir_cmd);
+ install_element(MS_NODE, &cfg_ms_no_clip_cmd);
+ install_element(MS_NODE, &cfg_ms_no_clir_cmd);
+ install_element(MS_NODE, &cfg_ms_sim_cmd);
+
+ install_element(MS_NODE, &cfg_testsim_cmd);
+ install_node(&testsim_node, config_write_dummy);
+ install_default(TESTSIM_NODE);
+ install_element(TESTSIM_NODE, &ournode_exit_cmd);
+ install_element(TESTSIM_NODE, &ournode_end_cmd);
+ install_element(TESTSIM_NODE, &cfg_test_imsi_cmd);
+ install_element(TESTSIM_NODE, &cfg_test_barr_cmd);
+ install_element(TESTSIM_NODE, &cfg_test_no_barr_cmd);
+ install_element(TESTSIM_NODE, &cfg_test_no_rplmn_cmd);
+ install_element(TESTSIM_NODE, &cfg_test_rplmn_cmd);
+ install_element(TESTSIM_NODE, &cfg_test_hplmn_cmd);
+
+ return 0;
+}
+
+void vty_notify(struct osmocom_ms *ms, const char *fmt, ...)
+{
+ struct telnet_connection *connection;
+ char buffer[1000];
+ va_list args;
+ struct vty *vty;
+
+ if (fmt) {
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
+ buffer[sizeof(buffer) - 1] = '\0';
+ va_end(args);
+
+ if (!buffer[0])
+ return;
+ }
+
+ llist_for_each_entry(connection, &active_connections, entry) {
+ vty = connection->vty;
+ if (!vty)
+ continue;
+ if (!fmt) {
+ vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name,
+ VTY_NEWLINE);
+ continue;
+ }
+ if (buffer[strlen(buffer) - 1] == '\n') {
+ buffer[strlen(buffer) - 1] = '\0';
+ vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE);
+ buffer[strlen(buffer)] = '\n';
+ } else
+ vty_out(vty, "%% %s", buffer);
+ }
+}
+
diff --git a/src/host/osmocon/.gitignore b/src/host/osmocon/.gitignore
new file mode 100644
index 00000000..7c58aae3
--- /dev/null
+++ b/src/host/osmocon/.gitignore
@@ -0,0 +1,10 @@
+*.o
+osmocon
+*.id*
+*.nam
+*.til
+*.dump
+*.bin
+*.log
+.version
+.tarball-version
diff --git a/src/host/osmocon/COPYING b/src/host/osmocon/COPYING
new file mode 100644
index 00000000..d511905c
--- /dev/null
+++ b/src/host/osmocon/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src/host/osmocon/Makefile.am b/src/host/osmocon/Makefile.am
new file mode 100644
index 00000000..8b0d4bf3
--- /dev/null
+++ b/src/host/osmocon/Makefile.am
@@ -0,0 +1,21 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+
+# versioning magic
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+
+sbin_PROGRAMS = osmocon osmoload
+
+# FIXME: sercomm needs to move into libosmocore or another shared lib
+INCLUDES += -I../../target/firmware/include/comm -I../../target/firmware/apps -DHOST_BUILD
+osmocon_SOURCES = osmocon.c tpu_debug.c ../../target/firmware/comm/sercomm.c
+osmocon_LDADD = $(LIBOSMOCORE_LIBS)
+
+osmoload_SOURCE = osmoload.c ../../target/firmware/comm/sercomm.c
+osmoload_LDADD = $(LIBOSMOCORE_LIBS)
diff --git a/src/host/osmocon/configure.ac b/src/host/osmocon/configure.ac
new file mode 100644
index 00000000..41308003
--- /dev/null
+++ b/src/host/osmocon/configure.ac
@@ -0,0 +1,25 @@
+dnl Process this file with autoconf to produce a configure script
+AC_INIT([osmocon],
+ m4_esyscmd([./git-version-gen .tarball-version]),
+ [baseband-devel@lists.osmocom.org])
+
+AM_INIT_AUTOMAKE([dist-bzip2])
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl checks for programs
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+
+dnl checks for libraries
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
+
+dnl checks for header files
+AC_HEADER_STDC
+
+dnl Checks for typedefs, structures and compiler characteristics
+
+AC_OUTPUT(
+ Makefile)
diff --git a/src/host/osmocon/git-version-gen b/src/host/osmocon/git-version-gen
new file mode 100755
index 00000000..652fac68
--- /dev/null
+++ b/src/host/osmocon/git-version-gen
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-01-28.01
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+ 1) ;;
+ *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || exit 1
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test -z "$v" \
+ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+ : # use $v
+elif
+ v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && case $v in
+ osmocon_[0-9]*) ;;
+ osmocon_v[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+ case $v in
+ *-*-*) : git describe is okay three part flavor ;;
+ *-*)
+ : git describe is older two part flavor
+ # Recreate the number of commits and rewrite such that the
+ # result is the same as if we were using the newer version
+ # of git describe.
+ vtag=`echo "$v" | sed 's/-.*//'`
+ numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+ v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+ ;;
+ esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+ v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`;
+else
+ v="UNKNOWN"
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/src/host/osmocon/memdump_convert.pl b/src/host/osmocon/memdump_convert.pl
new file mode 100755
index 00000000..3d18a0b3
--- /dev/null
+++ b/src/host/osmocon/memdump_convert.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+my $num_line = 0;
+my $num_hex = 0;
+my $oldaddr;
+
+while (my $line = <STDIN>) {
+ chomp($line);
+ $num_line++;
+ my (@hex) = $line =~ /(\w{8}): (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8})/;
+ my $addr = hex(shift @hex);
+ if ($addr != 0 && $addr != $oldaddr + 0x20) {
+ printf(STDERR "gap of %u between 0x%08x and 0x%08x\n%s\n",
+ $addr - $oldaddr, $addr, $oldaddr, $line);
+ }
+ foreach my $h (@hex) {
+ $num_hex++;
+ # poor mans endian conversion
+ my ($a, $b, $c, $d) = $h =~/(\w\w)(\w\w)(\w\w)(\w\w)/;
+ my $h_reorder = $d . $c . $b . $a;
+ # convert into actual binary number
+ my $tmp = pack('H8', $h_reorder);
+ syswrite(STDOUT, $tmp, 4);
+ }
+ $oldaddr = $addr;
+}
+
+printf(STDERR "num lines/num hex: %u/%u\n", $num_line, $num_hex);
+
diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c
new file mode 100644
index 00000000..1f4d9ca8
--- /dev/null
+++ b/src/host/osmocon/osmocon.c
@@ -0,0 +1,1507 @@
+/* osmocon */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <sercomm.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/select.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+
+#include <arpa/inet.h>
+
+#define MODEM_BAUDRATE B115200
+#define MAX_DNLOAD_SIZE 0xFFFF
+#define MAX_HDR_SIZE 128
+#define MAGIC_OFFSET 0x3be2
+
+#define BEACON_INTERVAL 50000
+#define ROMLOAD_INIT_BAUDRATE B19200
+#define ROMLOAD_DL_BAUDRATE B115200
+#define ROMLOAD_BLOCK_HDR_LEN 10
+#define ROMLOAD_ADDRESS 0x820000
+
+#define MTK_INIT_BAUDRATE B19200
+#define MTK_ADDRESS 0x40001400
+#define MTK_BLOCK_SIZE 1024
+
+struct tool_server *tool_server_for_dlci[256];
+
+/**
+ * a connection from some other tool
+ */
+struct tool_connection {
+ struct tool_server *server;
+ struct llist_head entry;
+ struct bsc_fd fd;
+};
+
+/**
+ * server for a tool
+ */
+struct tool_server {
+ struct bsc_fd bfd;
+ uint8_t dlci;
+ struct llist_head connections;
+};
+
+
+enum dnload_state {
+ WAITING_PROMPT1,
+ WAITING_PROMPT2,
+ DOWNLOADING,
+};
+
+enum romload_state {
+ WAITING_IDENTIFICATION,
+ WAITING_PARAM_ACK,
+ SENDING_BLOCKS,
+ SENDING_LAST_BLOCK,
+ LAST_BLOCK_SENT,
+ WAITING_BLOCK_ACK,
+ WAITING_CHECKSUM_ACK,
+ WAITING_BRANCH_ACK,
+ FINISHED,
+};
+
+enum mtk_state {
+ MTK_INIT_1,
+ MTK_INIT_2,
+ MTK_INIT_3,
+ MTK_INIT_4,
+ MTK_WAIT_WRITE_ACK,
+ MTK_WAIT_ADDR_ACK,
+ MTK_WAIT_SIZE_ACK,
+ MTK_SENDING_BLOCKS,
+ MTK_WAIT_BRANCH_CMD_ACK,
+ MTK_WAIT_BRANCH_ADDR_ACK,
+ MTK_FINISHED,
+};
+
+enum dnload_mode {
+ MODE_C123,
+ MODE_C123xor,
+ MODE_C140,
+ MODE_C140xor,
+ MODE_C155,
+ MODE_ROMLOAD,
+ MODE_MTK,
+};
+
+struct dnload {
+ enum dnload_state state;
+ enum romload_state romload_state;
+ enum mtk_state mtk_state;
+ enum dnload_mode mode;
+ struct bsc_fd serial_fd;
+ char *filename;
+
+ int print_hdlc;
+
+ /* data to be downloaded */
+ uint8_t *data;
+ int data_len;
+
+ uint8_t *write_ptr;
+
+ /* romload: block to be downloaded */
+ uint8_t *block;
+ int block_len;
+ uint8_t block_number;
+ uint16_t block_payload_size;
+ int romload_dl_checksum;
+ uint8_t *block_ptr;
+ uint8_t load_address[4];
+
+ uint8_t mtk_send_size[4];
+ int block_count;
+ int echo_bytecount;
+
+ struct tool_server layer2_server;
+ struct tool_server loader_server;
+};
+
+
+static struct dnload dnload;
+static struct timer_list tick_timer;
+
+/* Compal ramloader specific */
+static const uint8_t phone_prompt1[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x01, 0x40 };
+static const uint8_t dnload_cmd[] = { 0x1b, 0xf6, 0x02, 0x00, 0x52, 0x01, 0x53 };
+static const uint8_t phone_prompt2[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x02, 0x43 };
+static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 };
+static const uint8_t phone_nack_magic[]= { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x57 };
+static const uint8_t phone_nack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x45, 0x53, 0x16 };
+static const uint8_t ftmtool[] = { 0x66, 0x74, 0x6d, 0x74, 0x6f, 0x6f, 0x6c };
+static const uint8_t phone_magic[] = { 0x31, 0x30, 0x30, 0x33 }; /* "1003" */
+
+/* The C123 has a hard-coded check inside the ramloader that requires the
+ * following bytes to be always the first four bytes of the image */
+static const uint8_t data_hdr_c123[] = { 0xee, 0x4c, 0x9f, 0x63 };
+
+/* The C155 doesn't have some strange restriction on what the first four bytes
+ * have to be, but it starts the ramloader in THUMB mode. We use the following
+ * four bytes to switch back to ARM mode:
+ 800100: 4778 bx pc
+ 800102: 46c0 nop ; (mov r8, r8)
+ */
+static const uint8_t data_hdr_c155[] = { 0x78, 0x47, 0xc0, 0x46 };
+
+/* Calypso romloader specific */
+static const uint8_t romload_ident_cmd[] = { 0x3c, 0x69 }; /* <i */
+static const uint8_t romload_abort_cmd[] = { 0x3c, 0x61 }; /* <a */
+static const uint8_t romload_write_cmd[] = { 0x3c, 0x77 }; /* <w */
+static const uint8_t romload_checksum_cmd[] = { 0x3c, 0x63 }; /* <c */
+static const uint8_t romload_branch_cmd[] = { 0x3c, 0x62 }; /* <b */
+static const uint8_t romload_ident_ack[] = { 0x3e, 0x69 }; /* >i */
+static const uint8_t romload_param_ack[] = { 0x3e, 0x70 }; /* >p */
+static const uint8_t romload_param_nack[] = { 0x3e, 0x50 }; /* >P */
+static const uint8_t romload_block_ack[] = { 0x3e, 0x77 }; /* >w */
+static const uint8_t romload_block_nack[] = { 0x3e, 0x57 }; /* >W */
+static const uint8_t romload_checksum_ack[] = { 0x3e, 0x63 }; /* >c */
+static const uint8_t romload_checksum_nack[] = { 0x3e, 0x43 }; /* >C */
+static const uint8_t romload_branch_ack[] = { 0x3e, 0x62 }; /* >b */
+static const uint8_t romload_branch_nack[] = { 0x3e, 0x42 }; /* >B */
+
+/* romload_param: {"<p", uint8_t baudrate, uint8_t dpll, uint16_t memory_config,
+ * uint8_t strobe_af, uint32_t uart_timeout} */
+
+static const uint8_t romload_param[] = { 0x3c, 0x70, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/* MTK romloader specific */
+static const uint8_t mtk_init_cmd[] = { 0xa0, 0x0a, 0x50, 0x05 };
+static const uint8_t mtk_init_resp[] = { 0x5f, 0xf5, 0xaf, 0xfa };
+static const uint8_t mtk_command[] = { 0xa1, 0xa2, 0xa4, 0xa8 };
+
+/* FIXME: this routine is more or less what openbsc/src/rs232:rs232_setup()
+ * does, we should move it to libosmocore at some point */
+static int serial_init(const char *serial_port)
+{
+ int rc, serial_fd, v24;
+ struct termios tio;
+
+ serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY);
+ if (serial_fd < 0) {
+ perror("cannot open serial port");
+ return serial_fd;
+ }
+
+ //fcntl(serial_fd, F_SETFL, 0);
+
+ /* Configure serial interface */
+ rc = tcgetattr(serial_fd, &tio);
+ if (rc < 0) {
+ perror("tcgetattr()");
+ return rc;
+ }
+ cfsetispeed(&tio, MODEM_BAUDRATE);
+ cfsetospeed(&tio, MODEM_BAUDRATE);
+ tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+ tio.c_cflag |= (CREAD | CLOCAL | CS8);
+ tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tio.c_iflag |= (INPCK | ISTRIP);
+ tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
+ tio.c_oflag &= ~(OPOST | ONLCR);
+ rc = tcsetattr(serial_fd, TCSANOW, &tio);
+ if (rc < 0) {
+ perror("tcsetattr()");
+ return rc;
+ }
+
+ /* set ready to read/write */
+ v24 = TIOCM_DTR | TIOCM_RTS;
+ rc = ioctl(serial_fd, TIOCMBIS, &v24);
+ if (rc < 0) {
+ perror("ioctl(TIOCMBIS)");
+ return rc;
+ }
+
+ return serial_fd;
+}
+
+static int serial_set_baudrate(speed_t baudrate)
+{
+ int rc;
+ struct termios tio;
+
+ rc = tcgetattr(dnload.serial_fd.fd, &tio);
+ if (rc < 0) {
+ perror("tcgetattr()");
+ return rc;
+ }
+ cfsetispeed(&tio, baudrate);
+ cfsetospeed(&tio, baudrate);
+
+ rc = tcsetattr(dnload.serial_fd.fd, TCSANOW, &tio);
+ return rc;
+}
+
+static void beacon_timer_cb(void *p)
+{
+ int rc;
+
+ if (dnload.romload_state == WAITING_IDENTIFICATION) {
+ printf("Sending Calypso romloader beacon...\n");
+ rc = write(dnload.serial_fd.fd, romload_ident_cmd,
+ sizeof(romload_ident_cmd));
+
+ if (!(rc == sizeof(romload_ident_cmd)))
+ printf("Error sending identification beacon\n");
+
+ bsc_schedule_timer(p, 0, BEACON_INTERVAL);
+ }
+}
+
+static void mtk_timer_cb(void *p)
+{
+ int rc;
+
+ if (dnload.mtk_state == MTK_INIT_1) {
+ printf("Sending MTK romloader beacon...\n");
+ rc = write(dnload.serial_fd.fd, &mtk_init_cmd[0], 1);
+
+ if (!(rc == 1))
+ printf("Error sending identification beacon\n");
+
+ bsc_schedule_timer(p, 0, BEACON_INTERVAL);
+ }
+}
+
+/* Read the to-be-downloaded file, prepend header and length, append XOR sum */
+int read_file(const char *filename)
+{
+ int fd, rc, i;
+ struct stat st;
+ const uint8_t *hdr = NULL;
+ int payload_size;
+ int hdr_len = 0;
+ uint8_t *file_data;
+ uint16_t tot_len;
+ uint8_t nibble;
+ uint8_t running_xor = 0x02;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("opening file");
+ exit(1);
+ }
+
+ rc = fstat(fd, &st);
+ if (st.st_size > MAX_DNLOAD_SIZE) {
+ fprintf(stderr, "The maximum file size is 64kBytes (%u bytes)\n",
+ MAX_DNLOAD_SIZE);
+ return -EFBIG;
+ }
+
+ if (dnload.data) {
+ free(dnload.data);
+ dnload.data = NULL;
+ }
+
+ if (dnload.mode == MODE_C140 || dnload.mode == MODE_C140xor) {
+ if (st.st_size < (MAGIC_OFFSET + sizeof(phone_magic)))
+ payload_size = MAGIC_OFFSET + sizeof(phone_magic);
+ else {
+ printf("\nThe filesize is larger than 15kb, code on "
+ "the magic address will be overwritten!\nUse "
+ "loader.bin and upload the application with "
+ "osmoload instead!\n\n");
+ payload_size = st.st_size;
+ }
+ } else
+ payload_size = st.st_size;
+
+ dnload.data = malloc(MAX_HDR_SIZE + payload_size);
+
+ if (!dnload.data) {
+ close(fd);
+ fprintf(stderr, "No memory\n");
+ return -ENOMEM;
+ }
+
+ /* copy in the header, if any */
+ switch (dnload.mode) {
+ case MODE_C155:
+ hdr = data_hdr_c155;
+ hdr_len = sizeof(data_hdr_c155);
+ break;
+ case MODE_C140:
+ case MODE_C140xor:
+ case MODE_C123:
+ case MODE_C123xor:
+ hdr = data_hdr_c123;
+ hdr_len = sizeof(data_hdr_c123);
+ break;
+ case MODE_ROMLOAD:
+ break;
+ default:
+ break;
+ }
+
+ if (hdr && hdr_len)
+ memcpy(dnload.data, hdr, hdr_len);
+
+ /* 2 bytes for length + header */
+ file_data = dnload.data + 2 + hdr_len;
+
+ /* write the length, keep running XOR */
+ tot_len = hdr_len + payload_size;
+ nibble = tot_len >> 8;
+ dnload.data[0] = nibble;
+ running_xor ^= nibble;
+ nibble = tot_len & 0xff;
+ dnload.data[1] = nibble;
+ running_xor ^= nibble;
+
+ if (hdr_len && hdr) {
+ memcpy(dnload.data+2, hdr, hdr_len);
+
+ for (i = 0; i < hdr_len; i++)
+ running_xor ^= hdr[i];
+ }
+
+ rc = read(fd, file_data, st.st_size);
+ if (rc < 0) {
+ perror("error reading file\n");
+ free(dnload.data);
+ dnload.data = NULL;
+ close(fd);
+ return -EIO;
+ }
+ if (rc < st.st_size) {
+ free(dnload.data);
+ dnload.data = NULL;
+ close(fd);
+ fprintf(stderr, "Short read of file (%d < %d)\n",
+ rc, (int)st.st_size);
+ return -EIO;
+ }
+
+ close(fd);
+
+ dnload.data_len = (file_data+payload_size) - dnload.data;
+
+ /* fill memory between data end and magic, add magic */
+ if(dnload.mode == MODE_C140 || dnload.mode == MODE_C140xor) {
+ if (st.st_size < MAGIC_OFFSET)
+ memset(file_data + st.st_size, 0x00,
+ payload_size - st.st_size);
+ memcpy(dnload.data + MAGIC_OFFSET, phone_magic,
+ sizeof(phone_magic));
+ }
+
+ /* calculate XOR sum */
+ for (i = 0; i < payload_size; i++)
+ running_xor ^= file_data[i];
+
+ dnload.data[dnload.data_len++] = running_xor;
+
+ /* initialize write pointer to start of data */
+ dnload.write_ptr = dnload.data;
+
+ printf("read_file(%s): file_size=%u, hdr_len=%u, dnload_len=%u\n",
+ filename, (int)st.st_size, hdr_len, dnload.data_len);
+
+ return 0;
+}
+
+static void hexdump(const uint8_t *data, unsigned int len)
+{
+ int n;
+
+ for (n=0; n < len; n++)
+ printf("%02x ", data[n]);
+ printf(" ");
+ for (n=0; n < len; n++)
+ if (isprint(data[n]))
+ putchar(data[n]);
+ else
+ putchar('.');
+ printf("\n");
+}
+
+static int romload_prepare_block(void)
+{
+ int rc, i;
+
+ int block_checksum = 5;
+ int remaining_bytes;
+ int fill_bytes;
+ uint8_t *block_data;
+ uint32_t block_address;
+
+ dnload.block_len = ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size;
+
+ /* if first block, allocate memory */
+ if (!dnload.block_number) {
+ dnload.block = malloc(dnload.block_len);
+ if (!dnload.block) {
+ fprintf(stderr, "No memory\n");
+ return -ENOMEM;
+ }
+ dnload.romload_dl_checksum = 0;
+ /* initialize write pointer to start of data */
+ dnload.write_ptr = dnload.data;
+ }
+
+ block_address = ROMLOAD_ADDRESS +
+ (dnload.block_number * dnload.block_payload_size);
+
+ /* prepare our block header (10 bytes) */
+ memcpy(dnload.block, romload_write_cmd, sizeof(romload_write_cmd));
+ dnload.block[2] = 0x01; /* block index */
+ /* should normally be the block number, but hangs when sending !0x01 */
+ dnload.block[3] = 0x01; /* dnload.block_number+1 */
+ dnload.block[4] = (dnload.block_payload_size >> 8) & 0xff;
+ dnload.block[5] = dnload.block_payload_size & 0xff;
+ dnload.block[6] = (block_address >> 24) & 0xff;
+ dnload.block[7] = (block_address >> 16) & 0xff;
+ dnload.block[8] = (block_address >> 8) & 0xff;
+ dnload.block[9] = block_address & 0xff;
+
+ block_data = dnload.block + ROMLOAD_BLOCK_HDR_LEN;
+ dnload.write_ptr = dnload.data + 2 +
+ (dnload.block_payload_size * dnload.block_number);
+
+ remaining_bytes = dnload.data_len - 3 -
+ (dnload.block_payload_size * dnload.block_number);
+
+ memcpy(block_data, dnload.write_ptr, dnload.block_payload_size);
+
+ if (remaining_bytes <= dnload.block_payload_size) {
+ fill_bytes = (dnload.block_payload_size - remaining_bytes);
+ printf("Preparing the last block, filling %i bytes,",
+ fill_bytes);
+ memset(block_data + remaining_bytes, 0x00, fill_bytes);
+ dnload.romload_state = SENDING_LAST_BLOCK;
+ } else {
+ dnload.romload_state = SENDING_BLOCKS;
+ printf("Preparing block %i,", dnload.block_number+1);
+ }
+
+ /* block checksum is lsb of ~(5 + block_size_lsb + all bytes of
+ * block_address + all data bytes) */
+ for (i = 5; i < ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size; i++)
+ block_checksum += dnload.block[i];
+
+ /* checksum is lsb of ~(sum of LSBs of all block checksums) */
+ printf(" block checksum is 0x%02x \n", ~(block_checksum) & 0xff);
+ dnload.romload_dl_checksum += ~(block_checksum) & 0xff;
+
+ /* initialize block pointer to start of block */
+ dnload.block_ptr = dnload.block;
+
+ dnload.block_number++;
+ dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ return rc;
+}
+
+static int mtk_prepare_block(void)
+{
+ int rc, i;
+
+ int remaining_bytes;
+ int fill_bytes;
+ uint8_t *block_data;
+ uint8_t tmp_byteswap;
+ uint32_t tmp_size;
+
+ dnload.block_len = MTK_BLOCK_SIZE;
+ dnload.echo_bytecount = 0;
+
+ /* if first block, allocate memory */
+ if (!dnload.block_number) {
+ dnload.block = malloc(dnload.block_len);
+ if (!dnload.block) {
+ fprintf(stderr, "No memory\n");
+ return -ENOMEM;
+ }
+
+ /* calculate the number of blocks we need to send */
+ dnload.block_count = (dnload.data_len-3) / MTK_BLOCK_SIZE;
+ /* add one more block if no multiple of blocksize */
+ if((dnload.data_len-3) % MTK_BLOCK_SIZE)
+ dnload.block_count++;
+
+ /* divide by 2, since we have to tell the mtk loader the size
+ * as count of uint16 (odd transfer sizes are not possible) */
+ tmp_size = (dnload.block_count * MTK_BLOCK_SIZE)/2;
+ dnload.mtk_send_size[0] = (tmp_size >> 24) & 0xff;
+ dnload.mtk_send_size[1] = (tmp_size >> 16) & 0xff;
+ dnload.mtk_send_size[2] = (tmp_size >> 8) & 0xff;
+ dnload.mtk_send_size[3] = tmp_size & 0xff;
+
+ /* initialize write pointer to start of data */
+ dnload.write_ptr = dnload.data;
+ }
+
+ block_data = dnload.block;
+ dnload.write_ptr = dnload.data + 2 +
+ (dnload.block_len * dnload.block_number);
+
+ remaining_bytes = dnload.data_len - 3 -
+ (dnload.block_len * dnload.block_number);
+
+ memcpy(block_data, dnload.write_ptr, MTK_BLOCK_SIZE);
+
+ if (remaining_bytes <= MTK_BLOCK_SIZE) {
+ fill_bytes = (MTK_BLOCK_SIZE - remaining_bytes);
+ printf("Preparing the last block, filling %i bytes\n",
+ fill_bytes);
+ memset(block_data + remaining_bytes, 0x00, fill_bytes);
+ dnload.romload_state = SENDING_LAST_BLOCK;
+ } else {
+ dnload.romload_state = SENDING_BLOCKS;
+ printf("Preparing block %i\n", dnload.block_number+1);
+ }
+
+ /* for the mtk romloader we need to swap MSB <-> LSB */
+ for (i = 0; i < dnload.block_len; i += 2) {
+ tmp_byteswap = dnload.block[i];
+ dnload.block[i] = dnload.block[i+1];
+ dnload.block[i+1] = tmp_byteswap;
+ }
+
+ /* initialize block pointer to start of block */
+ dnload.block_ptr = dnload.block;
+
+ dnload.block_number++;
+ return rc;
+}
+
+static int handle_write_block(void)
+{
+ int bytes_left, write_len, rc;
+
+ printf("handle_write_block(): ");
+
+ if (dnload.block_ptr >= dnload.block + dnload.block_len) {
+ printf("Block %i finished\n", dnload.block_number);
+ dnload.write_ptr = dnload.data;
+ dnload.serial_fd.when &= ~BSC_FD_WRITE;
+ if (dnload.romload_state == SENDING_LAST_BLOCK) {
+ dnload.romload_state = LAST_BLOCK_SENT;
+ printf("Finished, sent %i blocks in total\n",
+ dnload.block_number);
+ } else {
+ dnload.romload_state = WAITING_BLOCK_ACK;
+ }
+
+ return 0;
+ }
+
+ /* try to write a maximum of block_len bytes */
+ bytes_left = (dnload.block + dnload.block_len) - dnload.block_ptr;
+ write_len = dnload.block_len;
+ if (bytes_left < dnload.block_len)
+ write_len = bytes_left;
+
+ rc = write(dnload.serial_fd.fd, dnload.block_ptr, write_len);
+ if (rc < 0) {
+ perror("Error during write");
+ return rc;
+ }
+
+ dnload.block_ptr += rc;
+
+ printf("%u bytes (%tu/%u)\n", rc, dnload.block_ptr - dnload.block,
+ dnload.block_len);
+
+ return 0;
+}
+
+#define WRITE_BLOCK 4096
+
+static int handle_write_dnload(void)
+{
+ int bytes_left, write_len, rc;
+ uint8_t xor_init = 0x02;
+
+ printf("handle_write(): ");
+ if (dnload.write_ptr == dnload.data) {
+ /* no bytes have been transferred yet */
+ switch (dnload.mode) {
+ case MODE_C155:
+ case MODE_C140xor:
+ case MODE_C123xor:
+ rc = write(dnload.serial_fd.fd, &xor_init, 1);
+ break;
+ default:
+ break;
+ }
+ } 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;
+ return 1;
+ }
+
+ /* try to write a maximum of WRITE_BLOCK bytes */
+ bytes_left = (dnload.data + dnload.data_len) - dnload.write_ptr;
+ write_len = WRITE_BLOCK;
+ if (bytes_left < WRITE_BLOCK)
+ write_len = bytes_left;
+
+ rc = write(dnload.serial_fd.fd, dnload.write_ptr, write_len);
+ if (rc < 0) {
+ perror("Error during write");
+ return rc;
+ }
+
+ dnload.write_ptr += rc;
+
+ printf("%u bytes (%tu/%u)\n", rc, dnload.write_ptr - dnload.data,
+ dnload.data_len);
+
+ return 0;
+}
+
+static int handle_sercomm_write(void)
+{
+ uint8_t c;
+
+ if (sercomm_drv_pull(&c) != 0) {
+ if (write(dnload.serial_fd.fd, &c, 1) != 1)
+ perror("short write");
+ } else
+ dnload.serial_fd.when &= ~BSC_FD_WRITE;
+
+ return 0;
+}
+
+static int handle_write(void)
+{
+ /* TODO: simplify this again (global state: downloading, sercomm) */
+ switch (dnload.mode) {
+ case MODE_ROMLOAD:
+ switch (dnload.romload_state) {
+ case SENDING_BLOCKS:
+ case SENDING_LAST_BLOCK:
+ return handle_write_block();
+ default:
+ return handle_sercomm_write();
+ }
+ break;
+ case MODE_MTK:
+ switch (dnload.mtk_state) {
+ case MTK_SENDING_BLOCKS:
+ return handle_write_block();
+ default:
+ return handle_sercomm_write();
+ }
+ break;
+ default:
+ switch (dnload.state) {
+ case DOWNLOADING:
+ return handle_write_dnload();
+ default:
+ return handle_sercomm_write();
+ }
+ }
+
+ return 0;
+}
+
+static uint8_t buffer[sizeof(phone_prompt1)];
+static uint8_t *bufptr = buffer;
+
+static void hdlc_send_to_phone(uint8_t dlci, uint8_t *data, int len)
+{
+ struct msgb *msg;
+ uint8_t *dest;
+
+ printf("hdlc_send_to_phone(dlci=%u): ", dlci);
+ hexdump(data, len);
+
+ if (len > 512) {
+ fprintf(stderr, "Too much data to send. %u\n", len);
+ return;
+ }
+
+ /* push the message into the stack */
+ msg = sercomm_alloc_msgb(512);
+ if (!msg) {
+ fprintf(stderr, "Failed to create data for the frame.\n");
+ return;
+ }
+
+ /* copy the data */
+ dest = msgb_put(msg, len);
+ memcpy(dest, data, len);
+
+ sercomm_sendmsg(dlci, msg);
+
+ dnload.serial_fd.when |= BSC_FD_WRITE;
+}
+
+static void hdlc_console_cb(uint8_t dlci, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(1, msg->data, msg->len);
+ msgb_free(msg);
+}
+
+static void hdlc_tool_cb(uint8_t dlci, struct msgb *msg)
+{
+ struct tool_server *srv = tool_server_for_dlci[dlci];
+
+ if(srv) {
+ struct tool_connection *con;
+ u_int16_t *len;
+
+ len = (u_int16_t *) msgb_push(msg, 2);
+ *len = htons(msg->len - sizeof(*len));
+
+ llist_for_each_entry(con, &srv->connections, entry) {
+ if (write(con->fd.fd, msg->data, msg->len) != msg->len) {
+ fprintf(stderr,
+ "Failed to write msg to the socket..\n");
+ continue;
+ }
+ }
+ }
+
+ msgb_free(msg);
+}
+
+static void print_hdlc(uint8_t *buffer, int length)
+{
+ int i;
+
+ for (i = 0; i < length; ++i)
+ if (sercomm_drv_rx_char(buffer[i]) == 0)
+ printf("Dropping sample '%c'\n", buffer[i]);
+}
+
+static int handle_buffer(int buf_used_len)
+{
+ int nbytes, buf_left;
+
+ buf_left = buf_used_len - (bufptr - buffer);
+ if (buf_left <= 0) {
+ memmove(buffer, buffer+1, buf_used_len-1);
+ bufptr -= 1;
+ buf_left = 1;
+ }
+
+ nbytes = read(dnload.serial_fd.fd, bufptr, buf_left);
+ if (nbytes <= 0)
+ return nbytes;
+
+ if (!dnload.print_hdlc) {
+ printf("got %i bytes from modem, ", nbytes);
+ printf("data looks like: ");
+ hexdump(bufptr, nbytes);
+ } else {
+ print_hdlc(bufptr, nbytes);
+ }
+
+ return nbytes;
+}
+
+/* Compal ramloader */
+static int handle_read(void)
+{
+ int rc, nbytes;
+
+ nbytes = handle_buffer(sizeof(buffer));
+ if (nbytes <= 0)
+ return nbytes;
+
+ if (!memcmp(buffer, phone_prompt1, sizeof(phone_prompt1))) {
+ printf("Received PROMPT1 from phone, responding with CMD\n");
+ dnload.print_hdlc = 0;
+ dnload.state = WAITING_PROMPT2;
+ rc = write(dnload.serial_fd.fd, dnload_cmd, sizeof(dnload_cmd));
+
+ /* re-read file */
+ rc = read_file(dnload.filename);
+ if (rc < 0) {
+ fprintf(stderr, "read_file(%s) failed with %d\n",
+ dnload.filename, rc);
+ exit(1);
+ }
+ } 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;
+ 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;
+ dnload.state = WAITING_PROMPT1;
+ dnload.write_ptr = dnload.data;
+ dnload.print_hdlc = 1;
+ } 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;
+ 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;
+ 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;
+ dnload.state = WAITING_PROMPT1;
+ dnload.write_ptr = dnload.data;
+ }
+ bufptr += nbytes;
+
+ return nbytes;
+}
+
+/* "Calypso non-secure romloader" */
+static int handle_read_romload(void)
+{
+ int rc, nbytes, buf_used_len;
+
+ /* virtually limit buffer length for romloader, since responses
+ * are shorter and vary in length */
+
+ switch (dnload.romload_state) {
+ case WAITING_PARAM_ACK:
+ buf_used_len = 4; /* ">p" + uint16_t len */
+ break;
+ case WAITING_CHECKSUM_ACK:
+ buf_used_len = 3; /* ">c" + uint8_t checksum */
+ break;
+ case FINISHED:
+ buf_used_len = sizeof(buffer);
+ break;
+ default:
+ buf_used_len = 2; /* ">*" */
+ }
+
+ nbytes = handle_buffer(buf_used_len);
+ if (nbytes <= 0)
+ return nbytes;
+
+ switch (dnload.romload_state) {
+ case WAITING_IDENTIFICATION:
+ if (memcmp(buffer, romload_ident_ack,
+ sizeof(romload_ident_ack)))
+ break;
+
+ printf("Received ident ack from phone, sending "
+ "parameter sequence\n");
+ dnload.print_hdlc = 1;
+ dnload.romload_state = WAITING_PARAM_ACK;
+ rc = write(dnload.serial_fd.fd, romload_param,
+ sizeof(romload_param));
+ /* re-read file */
+ rc = read_file(dnload.filename);
+ if (rc < 0) {
+ fprintf(stderr, "read_file(%s) failed with %d\n",
+ dnload.filename, rc);
+ exit(1);
+ }
+ break;
+ case WAITING_PARAM_ACK:
+ if (memcmp(buffer, romload_param_ack,
+ sizeof(romload_param_ack)))
+ break;
+
+ printf("Received parameter ack from phone, "
+ "starting download\n");
+ serial_set_baudrate(ROMLOAD_DL_BAUDRATE);
+
+ /* using the max blocksize the phone tells us */
+ dnload.block_payload_size = ((buffer[3] << 8) + buffer[2]);
+ printf("Used blocksize for download is %i bytes\n",
+ dnload.block_payload_size);
+ dnload.block_payload_size -= ROMLOAD_BLOCK_HDR_LEN;
+ dnload.romload_state = SENDING_BLOCKS;
+ dnload.block_number = 0;
+ romload_prepare_block();
+ bufptr -= 2;
+ break;
+ case WAITING_BLOCK_ACK:
+ case LAST_BLOCK_SENT:
+ if (!memcmp(buffer, romload_block_ack,
+ sizeof(romload_block_ack))) {
+ printf("Received block ack from phone\n");
+ if (dnload.romload_state == LAST_BLOCK_SENT) {
+ /* send the checksum */
+ uint8_t final_checksum =
+ (~(dnload.romload_dl_checksum) & 0xff);
+ printf("Sending checksum: 0x%02x \n",
+ final_checksum);
+ rc = write(dnload.serial_fd.fd,
+ romload_checksum_cmd,
+ sizeof(romload_checksum_cmd));
+ rc = write(dnload.serial_fd.fd,
+ &final_checksum, 1);
+ dnload.romload_state = WAITING_CHECKSUM_ACK;
+ } else
+ romload_prepare_block();
+ } else if (!memcmp(buffer, romload_block_nack,
+ sizeof(romload_block_nack))) {
+ printf("Received block nack from phone, "
+ "something went wrong, aborting\n");
+ serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
+ dnload.romload_state = WAITING_IDENTIFICATION;
+ bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL);
+ }
+ break;
+ case WAITING_CHECKSUM_ACK:
+ if (!memcmp(buffer, romload_checksum_ack,
+ sizeof(romload_checksum_ack))) {
+ printf("Checksum on phone side matches, "
+ "let's branch to your code\n");
+ printf("Branching to 0x%08x\n", ROMLOAD_ADDRESS);
+
+ rc = write(dnload.serial_fd.fd, romload_branch_cmd,
+ sizeof(romload_branch_cmd));
+ rc = write(dnload.serial_fd.fd, &dnload.load_address,
+ sizeof(dnload.load_address));
+ dnload.romload_state = WAITING_BRANCH_ACK;
+ bufptr -= 1;
+ } else if (!memcmp(buffer, romload_checksum_nack,
+ sizeof(romload_checksum_nack))) {
+ printf("Checksum on phone side (0x%02x) doesn't "
+ "match ours, aborting\n", ~buffer[2]);
+ serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
+ dnload.romload_state = WAITING_IDENTIFICATION;
+ bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL);
+ bufptr -= 1;
+ }
+ break;
+ case WAITING_BRANCH_ACK:
+ 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;
+ dnload.romload_state = FINISHED;
+ dnload.write_ptr = dnload.data;
+ dnload.print_hdlc = 1;
+ } else if (!memcmp(buffer, romload_branch_nack,
+ sizeof(romload_branch_nack))) {
+ printf("Received branch nack, aborting\n");
+ serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
+ dnload.romload_state = WAITING_IDENTIFICATION;
+ bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL);
+ }
+ break;
+ default:
+ break;
+ }
+
+ bufptr += nbytes;
+ return nbytes;
+}
+
+/* MTK romloader */
+static int handle_read_mtk(void)
+{
+ int rc, nbytes, buf_used_len;
+
+ switch (dnload.mtk_state) {
+ case MTK_WAIT_ADDR_ACK:
+ case MTK_WAIT_SIZE_ACK:
+ case MTK_WAIT_BRANCH_ADDR_ACK:
+ buf_used_len = 4;
+ break;
+ case MTK_FINISHED:
+ buf_used_len = sizeof(buffer);
+ break;
+ default:
+ buf_used_len = 1;
+ }
+
+ nbytes = handle_buffer(buf_used_len);
+ if (nbytes <= 0)
+ return nbytes;
+
+ switch (dnload.mtk_state) {
+ case MTK_INIT_1:
+ if (!(buffer[0] == mtk_init_resp[0]))
+ break;
+ dnload.mtk_state = MTK_INIT_2;
+ printf("Received init magic byte 1\n");
+ rc = write(dnload.serial_fd.fd, &mtk_init_cmd[1], 1);
+ break;
+ case MTK_INIT_2:
+ if (!(buffer[0] == mtk_init_resp[1]))
+ break;
+ dnload.mtk_state = MTK_INIT_3;
+ printf("Received init magic byte 2\n");
+ rc = write(dnload.serial_fd.fd, &mtk_init_cmd[2], 1);
+ break;
+ case MTK_INIT_3:
+ if (!(buffer[0] == mtk_init_resp[2]))
+ break;
+ dnload.mtk_state = MTK_INIT_4;
+ printf("Received init magic byte 3\n");
+ rc = write(dnload.serial_fd.fd, &mtk_init_cmd[3], 1);
+ break;
+ case MTK_INIT_4:
+ if (!(buffer[0] == mtk_init_resp[3]))
+ break;
+ dnload.mtk_state = MTK_WAIT_WRITE_ACK;
+ printf("Received init magic byte 4, requesting write\n");
+ rc = write(dnload.serial_fd.fd, &mtk_command[0], 1);
+ break;
+ case MTK_WAIT_WRITE_ACK:
+ if (!(buffer[0] == mtk_command[0]))
+ break;
+ dnload.mtk_state = MTK_WAIT_ADDR_ACK;
+ printf("Received write ack, sending load address\n");
+
+ rc = write(dnload.serial_fd.fd, &dnload.load_address,
+ sizeof(dnload.load_address));
+ break;
+ case MTK_WAIT_ADDR_ACK:
+ if (memcmp(buffer, dnload.load_address,
+ sizeof(dnload.load_address)))
+ break;
+ printf("Received address ack from phone, sending loadsize\n");
+ /* re-read file */
+ rc = read_file(dnload.filename);
+ if (rc < 0) {
+ fprintf(stderr, "read_file(%s) failed with %d\n",
+ dnload.filename, rc);
+ exit(1);
+ }
+ dnload.block_number = 0;
+ mtk_prepare_block();
+ dnload.mtk_state = MTK_WAIT_SIZE_ACK;
+ rc = write(dnload.serial_fd.fd, &dnload.mtk_send_size,
+ sizeof(dnload.mtk_send_size));
+ break;
+ case MTK_WAIT_SIZE_ACK:
+ if (memcmp(buffer, dnload.mtk_send_size,
+ sizeof(dnload.mtk_send_size)))
+ break;
+ printf("Received size ack\n");
+ dnload.print_hdlc = 1;
+ dnload.mtk_state = MTK_SENDING_BLOCKS;
+ dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ bufptr -= 3;
+ break;
+ case MTK_SENDING_BLOCKS:
+ if (!(buffer[0] == dnload.block[dnload.echo_bytecount]))
+ printf("Warning: Byte %i of Block %i doesn't match,"
+ " check your serial connection!\n",
+ dnload.echo_bytecount, dnload.block_number);
+ dnload.echo_bytecount++;
+
+ if ((dnload.echo_bytecount+1) > MTK_BLOCK_SIZE) {
+ if ( dnload.block_number == dnload.block_count) {
+ rc = write(dnload.serial_fd.fd,
+ &mtk_command[3], 1);
+ printf("Sending branch command\n");
+ dnload.print_hdlc = 0;
+ dnload.mtk_state = MTK_WAIT_BRANCH_CMD_ACK;
+ break;
+ }
+ printf("Received Block %i preparing next block\n",
+ dnload.block_number);
+ mtk_prepare_block();
+ dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE;
+ }
+ break;
+ case MTK_WAIT_BRANCH_CMD_ACK:
+ if (!(buffer[0] == mtk_command[3]))
+ break;
+ dnload.mtk_state = MTK_WAIT_BRANCH_ADDR_ACK;
+ printf("Received branch command ack, sending address\n");
+
+ rc = write(dnload.serial_fd.fd, &dnload.load_address,
+ sizeof(dnload.load_address));
+ break;
+ case MTK_WAIT_BRANCH_ADDR_ACK:
+ if (memcmp(buffer, dnload.load_address,
+ sizeof(dnload.load_address)))
+ break;
+ printf("Received branch address ack, code should run now\n");
+
+ /* uncomment this once we have working uart driver & sercomm */
+ //dnload.mtk_state = MTK_FINISHED;
+ //dnload.serial_fd.when = BSC_FD_READ;
+ //dnload.print_hdlc = 1;
+ break;
+ default:
+ break;
+ }
+
+ bufptr += nbytes;
+ return nbytes;
+}
+
+static int serial_read(struct bsc_fd *fd, unsigned int flags)
+{
+ int rc;
+ if (flags & BSC_FD_READ) {
+ switch (dnload.mode) {
+ case MODE_ROMLOAD:
+ rc = handle_read_romload();
+ break;
+ case MODE_MTK:
+ rc = handle_read_mtk();
+ break;
+ default:
+ rc = handle_read();
+ break;
+ }
+ if (rc == 0)
+ exit(2);
+ }
+
+ if (flags & BSC_FD_WRITE) {
+ rc = handle_write();
+ if (rc == 1)
+ dnload.state = WAITING_PROMPT1;
+ }
+ return 0;
+}
+
+static int parse_mode(const char *arg)
+{
+ if (!strcasecmp(arg, "c123"))
+ return MODE_C123;
+ else if (!strcasecmp(arg, "c123xor"))
+ return MODE_C123xor;
+ else if (!strcasecmp(arg, "c140"))
+ return MODE_C140;
+ else if (!strcasecmp(arg, "c140xor"))
+ return MODE_C140xor;
+ else if (!strcasecmp(arg, "c155"))
+ return MODE_C155;
+ else if (!strcasecmp(arg, "romload"))
+ return MODE_ROMLOAD;
+ else if (!strcasecmp(arg, "mtk"))
+ return MODE_MTK;
+
+ return -1;
+}
+
+#define HELP_TEXT \
+ "[ -v | -h ] [ -p /dev/ttyXXXX ] [ -s /tmp/osmocom_l2 ]\n" \
+ "\t\t[ -l /tmp/osmocom_loader ]\n" \
+ "\t\t[ -m {c123,c123xor,c140,c140xor,c155,romload,mtk} ]\n" \
+ "\t\t file.bin\n\n" \
+ "* Open serial port /dev/ttyXXXX (connected to your phone)\n" \
+ "* Perform handshaking with the ramloader in the phone\n" \
+ "* Download file.bin to the attached phone (base address 0x00800100)\n"
+
+static int usage(const char *name)
+{
+ printf("Usage: %s ", name);
+ printf(HELP_TEXT);
+ exit(2);
+}
+
+static int version(const char *name)
+{
+ printf("%s version %s\n", name, PACKAGE_VERSION);
+ exit(2);
+}
+
+static int un_tool_read(struct bsc_fd *fd, unsigned int flags)
+{
+ int rc, c;
+ u_int16_t length = 0xffff;
+ u_int8_t buf[4096];
+ struct tool_connection *con = (struct tool_connection *)fd->data;
+
+ c = 0;
+ while(c < 2) {
+ rc = read(fd->fd, &buf + c, 2 - c);
+ if(rc == 0) {
+ // disconnect
+ goto close;
+ }
+ if(rc < 0) {
+ if(errno == EAGAIN) {
+ continue;
+ }
+ fprintf(stderr, "Err from socket: %s\n", strerror(errno));
+ goto close;
+ }
+ c += rc;
+ }
+
+ length = ntohs(*(u_int16_t*)buf);
+
+ c = 0;
+ while(c < length) {
+ rc = read(fd->fd, &buf + c, length - c);
+ if(rc == 0) {
+ // disconnect
+ goto close;
+ }
+ if(rc < 0) {
+ if(errno == EAGAIN) {
+ continue;
+ }
+ fprintf(stderr, "Err from socket: %s\n", strerror(errno));
+ goto close;
+ }
+ c += rc;
+ }
+
+ hdlc_send_to_phone(con->server->dlci, buf, length);
+
+ return 0;
+close:
+
+ close(fd->fd);
+ bsc_unregister_fd(fd);
+ llist_del(&con->entry);
+ talloc_free(con);
+ return -1;
+}
+
+/* accept a new connection */
+static int tool_accept(struct bsc_fd *fd, unsigned int flags)
+{
+ struct tool_server *srv = (struct tool_server *)fd->data;
+ struct tool_connection *con;
+ struct sockaddr_un un_addr;
+ socklen_t len;
+ int rc;
+
+ len = sizeof(un_addr);
+ rc = accept(fd->fd, (struct sockaddr *) &un_addr, &len);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to accept a new connection.\n");
+ return -1;
+ }
+
+ con = talloc_zero(NULL, struct tool_connection);
+ if (!con) {
+ fprintf(stderr, "Failed to create tool connection.\n");
+ 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;
+ if (bsc_register_fd(&con->fd) != 0) {
+ fprintf(stderr, "Failed to register the fd.\n");
+ return -1;
+ }
+
+ llist_add(&con->entry, &srv->connections);
+ return 0;
+}
+
+/*
+ * Register and start a tool server
+ */
+static int register_tool_server(struct tool_server *ts,
+ const char *path,
+ uint8_t dlci)
+{
+ struct bsc_fd *bfd = &ts->bfd;
+ struct sockaddr_un local;
+ unsigned int namelen;
+ int rc;
+
+ bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (bfd->fd < 0) {
+ fprintf(stderr, "Failed to create Unix Domain Socket.\n");
+ return -1;
+ }
+
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, path, sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+ unlink(local.sun_path);
+
+ /* we use the same magic that X11 uses in Xtranssock.c for
+ * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+ local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+ namelen = SUN_LEN(&local);
+#else
+ namelen = strlen(local.sun_path) +
+ offsetof(struct sockaddr_un, sun_path);
+#endif
+
+ rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
+ if (rc != 0) {
+ fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
+ local.sun_path);
+ return -1;
+ }
+
+ if (listen(bfd->fd, 0) != 0) {
+ fprintf(stderr, "Failed to listen.\n");
+ return -1;
+ }
+
+ bfd->when = BSC_FD_READ;
+ bfd->cb = tool_accept;
+ bfd->data = ts;
+
+ ts->dlci = dlci;
+ INIT_LLIST_HEAD(&ts->connections);
+
+ tool_server_for_dlci[dlci] = ts;
+
+ sercomm_register_rx_cb(dlci, hdlc_tool_cb);
+
+ if (bsc_register_fd(bfd) != 0) {
+ fprintf(stderr, "Failed to register the bfd.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+extern void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg);
+
+int main(int argc, char **argv)
+{
+ int opt, flags;
+ uint32_t tmp_load_address = 0;
+ const char *serial_dev = "/dev/ttyUSB1";
+ const char *layer2_un_path = "/tmp/osmocom_l2";
+ const char *loader_un_path = "/tmp/osmocom_loader";
+
+ dnload.mode = MODE_C123;
+
+ while ((opt = getopt(argc, argv, "hl:p:m:s:v")) != -1) {
+ switch (opt) {
+ case 'p':
+ serial_dev = optarg;
+ break;
+ case 'm':
+ dnload.mode = parse_mode(optarg);
+ if (dnload.mode < 0)
+ usage(argv[0]);
+ break;
+ case 's':
+ layer2_un_path = optarg;
+ break;
+ case 'l':
+ loader_un_path = optarg;
+ break;
+ case 'v':
+ version(argv[0]);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (argc <= optind) {
+ fprintf(stderr, "You have to specify the filename\n");
+ usage(argv[0]);
+ }
+
+ dnload.filename = argv[optind];
+
+ dnload.serial_fd.fd = serial_init(serial_dev);
+ if (dnload.serial_fd.fd < 0) {
+ fprintf(stderr, "Cannot open serial device %s\n", serial_dev);
+ exit(1);
+ }
+
+ if (bsc_register_fd(&dnload.serial_fd) != 0) {
+ fprintf(stderr, "Failed to register the serial.\n");
+ exit(1);
+ }
+
+ /* Set serial socket to non-blocking mode of operation */
+ flags = fcntl(dnload.serial_fd.fd, F_GETFL);
+ 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);
+ sercomm_register_rx_cb(SC_DLCI_DEBUG, hdlc_tpudbg_cb);
+
+ /* unix domain socket handling */
+ if (register_tool_server(&dnload.layer2_server, layer2_un_path,
+ SC_DLCI_L1A_L23) != 0)
+ exit(1);
+
+ if (register_tool_server(&dnload.loader_server, loader_un_path,
+ SC_DLCI_LOADER) != 0)
+ exit(1);
+
+ /* if in romload mode, start our beacon timer */
+ if (dnload.mode == MODE_ROMLOAD) {
+ tmp_load_address = ROMLOAD_ADDRESS;
+ serial_set_baudrate(ROMLOAD_INIT_BAUDRATE);
+ tick_timer.cb = &beacon_timer_cb;
+ tick_timer.data = &tick_timer;
+ bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL);
+ }
+ else if (dnload.mode == MODE_MTK) {
+ tmp_load_address = MTK_ADDRESS;
+ serial_set_baudrate(MTK_INIT_BAUDRATE);
+ tick_timer.cb = &mtk_timer_cb;
+ tick_timer.data = &tick_timer;
+ bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL);
+ }
+
+ dnload.load_address[0] = (tmp_load_address >> 24) & 0xff;
+ dnload.load_address[1] = (tmp_load_address >> 16) & 0xff;
+ dnload.load_address[2] = (tmp_load_address >> 8) & 0xff;
+ dnload.load_address[3] = tmp_load_address & 0xff;
+
+ while (1)
+ bsc_select_main(0);
+
+ close(dnload.serial_fd.fd);
+
+ exit(0);
+}
diff --git a/src/host/osmocon/osmoload.c b/src/host/osmocon/osmoload.c
new file mode 100644
index 00000000..ecee8b36
--- /dev/null
+++ b/src/host/osmocon/osmoload.c
@@ -0,0 +1,198 @@
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <arpa/inet.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/select.h>
+
+#include <loader/protocol.h>
+
+#define MSGB_MAX 256
+
+#define DEFAULT_SOCKET "/tmp/osmocom_loader"
+
+static struct bsc_fd connection;
+
+static int usage(const char *name)
+{
+ printf("\nUsage: %s [ -v | -h ] [ -m {c123,c155} ] [ -l /tmp/osmocom_loader ] COMMAND\n", name);
+ exit(2);
+}
+
+static int version(const char *name)
+{
+ //printf("\n%s version %s\n", name, VERSION);
+ exit(2);
+}
+
+static void hexdump(const uint8_t *data, unsigned int len)
+{
+ const uint8_t *bufptr = data;
+ int n;
+
+ for (n=0; n < len; n++, bufptr++)
+ printf("%02x ", *bufptr);
+ printf("\n");
+}
+
+static void
+loader_send_request(struct msgb *msg) {
+ int rc;
+ u_int16_t len = htons(msg->len);
+
+ printf("Sending %d bytes ", msg->len);
+ hexdump(msg->data, msg->len);
+
+ rc = write(connection.fd, &len, sizeof(len));
+ if(rc != sizeof(len)) {
+ fprintf(stderr, "Error writing.\n");
+ exit(2);
+ }
+
+ rc = write(connection.fd, msg->data, msg->len);
+ if(rc != msg->len) {
+ fprintf(stderr, "Error writing.\n");
+ exit(2);
+ }
+}
+
+static void
+loader_handle_reply(struct msgb *msg) {
+ printf("Received ");
+ hexdump(msg->data, msg->len);
+}
+
+static int
+loader_read_cb(struct bsc_fd *fd, unsigned int flags) {
+ struct msgb *msg;
+ u_int16_t len;
+ int rc;
+
+ msg = msgb_alloc(MSGB_MAX, "loader");
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate msg.\n");
+ return -1;
+ }
+
+ rc = read(fd->fd, &len, sizeof(len));
+ if (rc < sizeof(len)) {
+ fprintf(stderr, "Short read. Error.\n");
+ exit(2);
+ }
+
+ if (ntohs(len) > MSGB_MAX) {
+ fprintf(stderr, "Length is too big: %u\n", ntohs(len));
+ msgb_free(msg);
+ return -1;
+ }
+
+ /* blocking read for the poor... we can starve in here... */
+ msg->l2h = msgb_put(msg, ntohs(len));
+ rc = read(fd->fd, msg->l2h, msgb_l2len(msg));
+ if (rc != msgb_l2len(msg)) {
+ fprintf(stderr, "Can not read data: rc: %d errno: %d\n", rc, errno);
+ msgb_free(msg);
+ return -1;
+ }
+
+ loader_handle_reply(msg);
+
+ msgb_free(msg);
+
+ return 0;
+}
+
+static void
+loader_connect(const char *socket_path) {
+ int rc;
+ struct sockaddr_un local;
+ struct bsc_fd *conn = &connection;
+
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+ conn->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (conn->fd < 0) {
+ fprintf(stderr, "Failed to create unix domain socket.\n");
+ exit(1);
+ }
+
+ rc = connect(conn->fd, (struct sockaddr *) &local,
+ sizeof(local.sun_family) + strlen(local.sun_path));
+ if (rc < 0) {
+ fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path);
+ exit(1);
+ }
+
+ conn->when = BSC_FD_READ;
+ conn->cb = loader_read_cb;
+ conn->data = NULL;
+
+ if (bsc_register_fd(conn) != 0) {
+ fprintf(stderr, "Failed to register fd.\n");
+ exit(1);
+ }
+}
+
+static void
+loader_command(char *name, int cmdc, char **cmdv) {
+ if(!cmdc) {
+ usage(name);
+ }
+
+ char *cmd = cmdv[0];
+
+ printf("Command %s\n", cmd);
+
+ if(!strcmp(cmd, "ping")) {
+ struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
+ msgb_put_u8(msg, LOADER_PING);
+ msgb_put_u8(msg, 0);
+ loader_send_request(msg);
+ msgb_free(msg);
+ } else {
+ printf("Unknown command '%s'\n", cmd);
+ usage(name);
+ }
+}
+
+int
+main(int argc, char **argv) {
+ int opt;
+ char *loader_un_path = "/tmp/osmocom_loader";
+
+ while((opt = getopt(argc, argv, "hl:m:v")) != -1) {
+ switch(opt) {
+ case 'l':
+ loader_un_path = optarg;
+ break;
+ case 'm':
+ puts("model selection not implemented");
+ exit(2);
+ break;
+ case 'v':
+ version(argv[0]);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ loader_connect(loader_un_path);
+
+ loader_command(argv[0], argc - optind, argv + optind);
+
+ return 0;
+}
diff --git a/src/host/osmocon/tpu_debug.c b/src/host/osmocon/tpu_debug.c
new file mode 100644
index 00000000..f54bd40f
--- /dev/null
+++ b/src/host/osmocon/tpu_debug.c
@@ -0,0 +1,138 @@
+/* Calypso TPU debugger, displays and decodes TPU instruction RAM */
+
+/* (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 <stdlib.h>
+#include <stdio.h>
+
+#include <osmocore/msgb.h>
+
+/* TPU disassembler begin */
+
+static const char *tpu_instr_name[] = {
+ [0] = "SLEEP",
+ [1] = "AT",
+ [2] = "OFFSET",
+ [3] = "SYNCHRO",
+ [4] = "MOVE",
+ [5] = "WAIT",
+ [6] = "UNDEFINED6",
+ [7] = "UNDEFINED7",
+};
+
+static const char *tpu_addr_name[0x1f] = {
+ [0] = "TSP_CTLR1",
+ [1] = "TSP_CTRL2",
+ [4] = "TSP_TX_1",
+ [3] = "TSP_TX_2",
+ [2] = "TSP_TX_3",
+ [5] = "TSP_TX_4",
+ [6] = "TSPACT_L",
+ [7] = "TSPACT_H",
+ [9] = "TSP_SET1",
+ [0xa] = "TSP_SET2",
+ [0xb] = "TSP_SET3",
+ [0x10] = "DSP_INT_PG",
+ [0x11] = "GAUGING_EN",
+};
+
+static uint8_t tpu_reg_cache[0x1f];
+static uint16_t tpu_qbit;
+
+static void tpu_show_instr(uint16_t tpu)
+{
+ uint16_t instr = tpu >> 13;
+ uint16_t param = tpu & 0x1fff;
+ uint16_t addr, data, bitlen;
+ uint32_t tsp_data;
+
+ tpu_qbit++;
+
+ printf("\t %04u %04x %s ", tpu_qbit, tpu, tpu_instr_name[instr]);
+ switch (instr) {
+ case 0:
+ tpu_qbit = 0;
+ default:
+ break;
+ case 1:
+ tpu_qbit = param;
+ printf("%u ", param);
+ break;
+ case 5:
+ tpu_qbit += param;
+ printf("%u ", param);
+ break;
+ case 2:
+ case 3:
+ printf("%u ", param);
+ break;
+ case 4:
+ addr = param & 0x1f;
+ data = param >> 5;
+ tpu_reg_cache[addr] = data;
+ printf("%10s=0x%04x ", tpu_addr_name[addr], data);
+ switch (addr) {
+ case 0:
+ bitlen = (data & 0x1f) + 1;
+ printf("DEV_IDX=%u, BITLEN=%u ", data >> 5, bitlen);
+ if (bitlen <= 8) {
+ tsp_data = tpu_reg_cache[4];
+ printf(" TSP_DATA=0x%02x ", tsp_data);
+ } else if (bitlen <= 16) {
+ tsp_data = tpu_reg_cache[3];
+ tsp_data |= tpu_reg_cache[4] << 8;
+ printf(" TSP_DATA=0x%04x ", tsp_data);
+ } else if (bitlen <= 24) {
+ tsp_data = tpu_reg_cache[2];
+ tsp_data |= tpu_reg_cache[3] << 8;
+ tsp_data |= tpu_reg_cache[4] << 16;
+ printf(" TSP_DATA=0x%06x ", tsp_data);
+ } else {
+ tsp_data = tpu_reg_cache[5];
+ tsp_data |= tpu_reg_cache[2] << 8;
+ tsp_data |= tpu_reg_cache[3] << 16;
+ tsp_data |= tpu_reg_cache[4] << 24;
+ printf(" TSP_DATA=0x%08x ", tsp_data);
+ }
+ break;
+ case 1:
+ if (data & 0x01)
+ printf("READ ");
+ if (data & 0x02)
+ printf("WRITE ");
+ break;
+ }
+ }
+ printf("\n");
+}
+
+void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg)
+{
+ uint32_t *fn = (uint32_t *) msg->data;
+ uint16_t *tpu;
+
+ printf("TPU FN %u\n", *fn);
+ for (tpu = (uint16_t *) (msg->data + 4); tpu < (uint16_t *) msg->tail; tpu++)
+ tpu_show_instr(*tpu);
+
+ msgb_free(msg);
+}
diff --git a/src/host/rita_pll/rita_pll.pl b/src/host/rita_pll/rita_pll.pl
new file mode 100755
index 00000000..7de1aec4
--- /dev/null
+++ b/src/host/rita_pll/rita_pll.pl
@@ -0,0 +1,111 @@
+#!/usr/bin/perl
+
+sub pll_rx($$$$$) {
+ my ($a, $b, $p, $r, $l) = @_;
+
+ return (($b*$p+$a)/($r*$l))*26;
+}
+
+sub pll_rx_low_band($$) {
+ my ($a, $b) = @_;
+ my $p = 64; my $r = 65; my $l = 4;
+ return pll_rx($a, $b, $p, $r, $l);
+}
+
+sub pll_rx_high_band($$) {
+ my ($a, $b) = @_;
+ my $p = 64; my $r = 65; my $l = 2;
+ return pll_rx($a, $b, $p, $r, $l);
+}
+
+sub pll_tx_gsm850_1($$) {
+ my ($a, $b) = @_;
+ my $p = 64; my $r = 55; my $l = 4; my $m = 26;
+
+ my $left = ((1/$l) - (1/$m));
+ my $right = (($b*$p+$a)/$r);
+
+ return $left * $right * 26;
+}
+
+sub pll_tx_gsm850_2($$) {
+ my ($a, $b) = @_;
+ my $p = 64; my $r = 30; my $l = 4; my $m = 52;
+
+ my $left = ((1/$l) - (1/$m));
+ my $right = (($b*$p+$a)/$r);
+
+ return $left * $right * 26;
+}
+
+sub pll_tx_gsm900($$) {
+ my ($a, $b) = @_;
+ my $p = 64; my $r = 35; my $l = 4; my $m = 52;
+
+ my $left = ((1/$l) + (1/$m));
+ my $right = (($b*$p+$a)/$r);
+
+ return $left * $right * 26;
+}
+
+sub pll_tx_high($$) {
+ my ($a, $b) = @_;
+ my $p = 64; my $r = 70; my $l = 2; my $m = 26;
+
+ my $left = ((1/$l) + (1/$m));
+ my $right = (($b*$p+$a)/$r);
+
+ return $left * $right * 26;
+}
+
+sub hr() {
+ printf("======================================================================\n");
+}
+
+printf("PLL Rx Low Band:\n");
+for (my $b = 135; $b <= 150; $b++) {
+ for (my $a = 0; $a <= 62; $a++) {
+ printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_low_band($a, $b), $a, $b);
+ }
+}
+
+hr();
+printf("PLL Rx High Band:\n");
+for (my $b = 141; $b <= 155; $b++) {
+ for (my $a = 0; $a <= 62; $a++) {
+ printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_high_band($a, $b), $a, $b);
+ }
+}
+
+hr();
+printf("PLL Tx GSM850_1\n");
+for (my $b = 128; $b <= 130; $b++) {
+ for (my $a = 0; $a <= 62; $a++) {
+ printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_1($a, $b), $a, $b);
+ }
+}
+
+hr();
+printf("PLL Tx GSM850_2\n");
+for (my $b = 65; $b <= 66; $b++) {
+ for (my $a = 0; $a <= 63; $a++) {
+ printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_2($a, $b), $a, $b);
+ }
+}
+
+hr();
+printf("PLL Tx GSM900\n");
+for (my $b = 68; $b <= 71; $b++) {
+ for (my $a = 0; $a <= 63; $a++) {
+ printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm900($a, $b), $a, $b);
+ }
+}
+
+hr();
+printf("PLL Tx GSM1800/1900\n");
+for (my $b = 133; $b <= 149; $b++) {
+ for (my $a = 0; $a <= 63; $a++) {
+ printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_high($a, $b), $a, $b);
+ }
+}
+
diff --git a/src/host/rita_pll/rita_pll.txt b/src/host/rita_pll/rita_pll.txt
new file mode 100644
index 00000000..cac2cac2
--- /dev/null
+++ b/src/host/rita_pll/rita_pll.txt
@@ -0,0 +1,3625 @@
+PLL Rx Low Band:
+Fout=864.00 (A=000, B=135)
+Fout=864.10 (A=001, B=135)
+Fout=864.20 (A=002, B=135)
+Fout=864.30 (A=003, B=135)
+Fout=864.40 (A=004, B=135)
+Fout=864.50 (A=005, B=135)
+Fout=864.60 (A=006, B=135)
+Fout=864.70 (A=007, B=135)
+Fout=864.80 (A=008, B=135)
+Fout=864.90 (A=009, B=135)
+Fout=865.00 (A=010, B=135)
+Fout=865.10 (A=011, B=135)
+Fout=865.20 (A=012, B=135)
+Fout=865.30 (A=013, B=135)
+Fout=865.40 (A=014, B=135)
+Fout=865.50 (A=015, B=135)
+Fout=865.60 (A=016, B=135)
+Fout=865.70 (A=017, B=135)
+Fout=865.80 (A=018, B=135)
+Fout=865.90 (A=019, B=135)
+Fout=866.00 (A=020, B=135)
+Fout=866.10 (A=021, B=135)
+Fout=866.20 (A=022, B=135)
+Fout=866.30 (A=023, B=135)
+Fout=866.40 (A=024, B=135)
+Fout=866.50 (A=025, B=135)
+Fout=866.60 (A=026, B=135)
+Fout=866.70 (A=027, B=135)
+Fout=866.80 (A=028, B=135)
+Fout=866.90 (A=029, B=135)
+Fout=867.00 (A=030, B=135)
+Fout=867.10 (A=031, B=135)
+Fout=867.20 (A=032, B=135)
+Fout=867.30 (A=033, B=135)
+Fout=867.40 (A=034, B=135)
+Fout=867.50 (A=035, B=135)
+Fout=867.60 (A=036, B=135)
+Fout=867.70 (A=037, B=135)
+Fout=867.80 (A=038, B=135)
+Fout=867.90 (A=039, B=135)
+Fout=868.00 (A=040, B=135)
+Fout=868.10 (A=041, B=135)
+Fout=868.20 (A=042, B=135)
+Fout=868.30 (A=043, B=135)
+Fout=868.40 (A=044, B=135)
+Fout=868.50 (A=045, B=135)
+Fout=868.60 (A=046, B=135)
+Fout=868.70 (A=047, B=135)
+Fout=868.80 (A=048, B=135)
+Fout=868.90 (A=049, B=135)
+Fout=869.00 (A=050, B=135)
+Fout=869.10 (A=051, B=135)
+Fout=869.20 (A=052, B=135)
+Fout=869.30 (A=053, B=135)
+Fout=869.40 (A=054, B=135)
+Fout=869.50 (A=055, B=135)
+Fout=869.60 (A=056, B=135)
+Fout=869.70 (A=057, B=135)
+Fout=869.80 (A=058, B=135)
+Fout=869.90 (A=059, B=135)
+Fout=870.00 (A=060, B=135)
+Fout=870.10 (A=061, B=135)
+Fout=870.20 (A=062, B=135)
+Fout=870.40 (A=000, B=136)
+Fout=870.50 (A=001, B=136)
+Fout=870.60 (A=002, B=136)
+Fout=870.70 (A=003, B=136)
+Fout=870.80 (A=004, B=136)
+Fout=870.90 (A=005, B=136)
+Fout=871.00 (A=006, B=136)
+Fout=871.10 (A=007, B=136)
+Fout=871.20 (A=008, B=136)
+Fout=871.30 (A=009, B=136)
+Fout=871.40 (A=010, B=136)
+Fout=871.50 (A=011, B=136)
+Fout=871.60 (A=012, B=136)
+Fout=871.70 (A=013, B=136)
+Fout=871.80 (A=014, B=136)
+Fout=871.90 (A=015, B=136)
+Fout=872.00 (A=016, B=136)
+Fout=872.10 (A=017, B=136)
+Fout=872.20 (A=018, B=136)
+Fout=872.30 (A=019, B=136)
+Fout=872.40 (A=020, B=136)
+Fout=872.50 (A=021, B=136)
+Fout=872.60 (A=022, B=136)
+Fout=872.70 (A=023, B=136)
+Fout=872.80 (A=024, B=136)
+Fout=872.90 (A=025, B=136)
+Fout=873.00 (A=026, B=136)
+Fout=873.10 (A=027, B=136)
+Fout=873.20 (A=028, B=136)
+Fout=873.30 (A=029, B=136)
+Fout=873.40 (A=030, B=136)
+Fout=873.50 (A=031, B=136)
+Fout=873.60 (A=032, B=136)
+Fout=873.70 (A=033, B=136)
+Fout=873.80 (A=034, B=136)
+Fout=873.90 (A=035, B=136)
+Fout=874.00 (A=036, B=136)
+Fout=874.10 (A=037, B=136)
+Fout=874.20 (A=038, B=136)
+Fout=874.30 (A=039, B=136)
+Fout=874.40 (A=040, B=136)
+Fout=874.50 (A=041, B=136)
+Fout=874.60 (A=042, B=136)
+Fout=874.70 (A=043, B=136)
+Fout=874.80 (A=044, B=136)
+Fout=874.90 (A=045, B=136)
+Fout=875.00 (A=046, B=136)
+Fout=875.10 (A=047, B=136)
+Fout=875.20 (A=048, B=136)
+Fout=875.30 (A=049, B=136)
+Fout=875.40 (A=050, B=136)
+Fout=875.50 (A=051, B=136)
+Fout=875.60 (A=052, B=136)
+Fout=875.70 (A=053, B=136)
+Fout=875.80 (A=054, B=136)
+Fout=875.90 (A=055, B=136)
+Fout=876.00 (A=056, B=136)
+Fout=876.10 (A=057, B=136)
+Fout=876.20 (A=058, B=136)
+Fout=876.30 (A=059, B=136)
+Fout=876.40 (A=060, B=136)
+Fout=876.50 (A=061, B=136)
+Fout=876.60 (A=062, B=136)
+Fout=876.80 (A=000, B=137)
+Fout=876.90 (A=001, B=137)
+Fout=877.00 (A=002, B=137)
+Fout=877.10 (A=003, B=137)
+Fout=877.20 (A=004, B=137)
+Fout=877.30 (A=005, B=137)
+Fout=877.40 (A=006, B=137)
+Fout=877.50 (A=007, B=137)
+Fout=877.60 (A=008, B=137)
+Fout=877.70 (A=009, B=137)
+Fout=877.80 (A=010, B=137)
+Fout=877.90 (A=011, B=137)
+Fout=878.00 (A=012, B=137)
+Fout=878.10 (A=013, B=137)
+Fout=878.20 (A=014, B=137)
+Fout=878.30 (A=015, B=137)
+Fout=878.40 (A=016, B=137)
+Fout=878.50 (A=017, B=137)
+Fout=878.60 (A=018, B=137)
+Fout=878.70 (A=019, B=137)
+Fout=878.80 (A=020, B=137)
+Fout=878.90 (A=021, B=137)
+Fout=879.00 (A=022, B=137)
+Fout=879.10 (A=023, B=137)
+Fout=879.20 (A=024, B=137)
+Fout=879.30 (A=025, B=137)
+Fout=879.40 (A=026, B=137)
+Fout=879.50 (A=027, B=137)
+Fout=879.60 (A=028, B=137)
+Fout=879.70 (A=029, B=137)
+Fout=879.80 (A=030, B=137)
+Fout=879.90 (A=031, B=137)
+Fout=880.00 (A=032, B=137)
+Fout=880.10 (A=033, B=137)
+Fout=880.20 (A=034, B=137)
+Fout=880.30 (A=035, B=137)
+Fout=880.40 (A=036, B=137)
+Fout=880.50 (A=037, B=137)
+Fout=880.60 (A=038, B=137)
+Fout=880.70 (A=039, B=137)
+Fout=880.80 (A=040, B=137)
+Fout=880.90 (A=041, B=137)
+Fout=881.00 (A=042, B=137)
+Fout=881.10 (A=043, B=137)
+Fout=881.20 (A=044, B=137)
+Fout=881.30 (A=045, B=137)
+Fout=881.40 (A=046, B=137)
+Fout=881.50 (A=047, B=137)
+Fout=881.60 (A=048, B=137)
+Fout=881.70 (A=049, B=137)
+Fout=881.80 (A=050, B=137)
+Fout=881.90 (A=051, B=137)
+Fout=882.00 (A=052, B=137)
+Fout=882.10 (A=053, B=137)
+Fout=882.20 (A=054, B=137)
+Fout=882.30 (A=055, B=137)
+Fout=882.40 (A=056, B=137)
+Fout=882.50 (A=057, B=137)
+Fout=882.60 (A=058, B=137)
+Fout=882.70 (A=059, B=137)
+Fout=882.80 (A=060, B=137)
+Fout=882.90 (A=061, B=137)
+Fout=883.00 (A=062, B=137)
+Fout=883.20 (A=000, B=138)
+Fout=883.30 (A=001, B=138)
+Fout=883.40 (A=002, B=138)
+Fout=883.50 (A=003, B=138)
+Fout=883.60 (A=004, B=138)
+Fout=883.70 (A=005, B=138)
+Fout=883.80 (A=006, B=138)
+Fout=883.90 (A=007, B=138)
+Fout=884.00 (A=008, B=138)
+Fout=884.10 (A=009, B=138)
+Fout=884.20 (A=010, B=138)
+Fout=884.30 (A=011, B=138)
+Fout=884.40 (A=012, B=138)
+Fout=884.50 (A=013, B=138)
+Fout=884.60 (A=014, B=138)
+Fout=884.70 (A=015, B=138)
+Fout=884.80 (A=016, B=138)
+Fout=884.90 (A=017, B=138)
+Fout=885.00 (A=018, B=138)
+Fout=885.10 (A=019, B=138)
+Fout=885.20 (A=020, B=138)
+Fout=885.30 (A=021, B=138)
+Fout=885.40 (A=022, B=138)
+Fout=885.50 (A=023, B=138)
+Fout=885.60 (A=024, B=138)
+Fout=885.70 (A=025, B=138)
+Fout=885.80 (A=026, B=138)
+Fout=885.90 (A=027, B=138)
+Fout=886.00 (A=028, B=138)
+Fout=886.10 (A=029, B=138)
+Fout=886.20 (A=030, B=138)
+Fout=886.30 (A=031, B=138)
+Fout=886.40 (A=032, B=138)
+Fout=886.50 (A=033, B=138)
+Fout=886.60 (A=034, B=138)
+Fout=886.70 (A=035, B=138)
+Fout=886.80 (A=036, B=138)
+Fout=886.90 (A=037, B=138)
+Fout=887.00 (A=038, B=138)
+Fout=887.10 (A=039, B=138)
+Fout=887.20 (A=040, B=138)
+Fout=887.30 (A=041, B=138)
+Fout=887.40 (A=042, B=138)
+Fout=887.50 (A=043, B=138)
+Fout=887.60 (A=044, B=138)
+Fout=887.70 (A=045, B=138)
+Fout=887.80 (A=046, B=138)
+Fout=887.90 (A=047, B=138)
+Fout=888.00 (A=048, B=138)
+Fout=888.10 (A=049, B=138)
+Fout=888.20 (A=050, B=138)
+Fout=888.30 (A=051, B=138)
+Fout=888.40 (A=052, B=138)
+Fout=888.50 (A=053, B=138)
+Fout=888.60 (A=054, B=138)
+Fout=888.70 (A=055, B=138)
+Fout=888.80 (A=056, B=138)
+Fout=888.90 (A=057, B=138)
+Fout=889.00 (A=058, B=138)
+Fout=889.10 (A=059, B=138)
+Fout=889.20 (A=060, B=138)
+Fout=889.30 (A=061, B=138)
+Fout=889.40 (A=062, B=138)
+Fout=889.60 (A=000, B=139)
+Fout=889.70 (A=001, B=139)
+Fout=889.80 (A=002, B=139)
+Fout=889.90 (A=003, B=139)
+Fout=890.00 (A=004, B=139)
+Fout=890.10 (A=005, B=139)
+Fout=890.20 (A=006, B=139)
+Fout=890.30 (A=007, B=139)
+Fout=890.40 (A=008, B=139)
+Fout=890.50 (A=009, B=139)
+Fout=890.60 (A=010, B=139)
+Fout=890.70 (A=011, B=139)
+Fout=890.80 (A=012, B=139)
+Fout=890.90 (A=013, B=139)
+Fout=891.00 (A=014, B=139)
+Fout=891.10 (A=015, B=139)
+Fout=891.20 (A=016, B=139)
+Fout=891.30 (A=017, B=139)
+Fout=891.40 (A=018, B=139)
+Fout=891.50 (A=019, B=139)
+Fout=891.60 (A=020, B=139)
+Fout=891.70 (A=021, B=139)
+Fout=891.80 (A=022, B=139)
+Fout=891.90 (A=023, B=139)
+Fout=892.00 (A=024, B=139)
+Fout=892.10 (A=025, B=139)
+Fout=892.20 (A=026, B=139)
+Fout=892.30 (A=027, B=139)
+Fout=892.40 (A=028, B=139)
+Fout=892.50 (A=029, B=139)
+Fout=892.60 (A=030, B=139)
+Fout=892.70 (A=031, B=139)
+Fout=892.80 (A=032, B=139)
+Fout=892.90 (A=033, B=139)
+Fout=893.00 (A=034, B=139)
+Fout=893.10 (A=035, B=139)
+Fout=893.20 (A=036, B=139)
+Fout=893.30 (A=037, B=139)
+Fout=893.40 (A=038, B=139)
+Fout=893.50 (A=039, B=139)
+Fout=893.60 (A=040, B=139)
+Fout=893.70 (A=041, B=139)
+Fout=893.80 (A=042, B=139)
+Fout=893.90 (A=043, B=139)
+Fout=894.00 (A=044, B=139)
+Fout=894.10 (A=045, B=139)
+Fout=894.20 (A=046, B=139)
+Fout=894.30 (A=047, B=139)
+Fout=894.40 (A=048, B=139)
+Fout=894.50 (A=049, B=139)
+Fout=894.60 (A=050, B=139)
+Fout=894.70 (A=051, B=139)
+Fout=894.80 (A=052, B=139)
+Fout=894.90 (A=053, B=139)
+Fout=895.00 (A=054, B=139)
+Fout=895.10 (A=055, B=139)
+Fout=895.20 (A=056, B=139)
+Fout=895.30 (A=057, B=139)
+Fout=895.40 (A=058, B=139)
+Fout=895.50 (A=059, B=139)
+Fout=895.60 (A=060, B=139)
+Fout=895.70 (A=061, B=139)
+Fout=895.80 (A=062, B=139)
+Fout=896.00 (A=000, B=140)
+Fout=896.10 (A=001, B=140)
+Fout=896.20 (A=002, B=140)
+Fout=896.30 (A=003, B=140)
+Fout=896.40 (A=004, B=140)
+Fout=896.50 (A=005, B=140)
+Fout=896.60 (A=006, B=140)
+Fout=896.70 (A=007, B=140)
+Fout=896.80 (A=008, B=140)
+Fout=896.90 (A=009, B=140)
+Fout=897.00 (A=010, B=140)
+Fout=897.10 (A=011, B=140)
+Fout=897.20 (A=012, B=140)
+Fout=897.30 (A=013, B=140)
+Fout=897.40 (A=014, B=140)
+Fout=897.50 (A=015, B=140)
+Fout=897.60 (A=016, B=140)
+Fout=897.70 (A=017, B=140)
+Fout=897.80 (A=018, B=140)
+Fout=897.90 (A=019, B=140)
+Fout=898.00 (A=020, B=140)
+Fout=898.10 (A=021, B=140)
+Fout=898.20 (A=022, B=140)
+Fout=898.30 (A=023, B=140)
+Fout=898.40 (A=024, B=140)
+Fout=898.50 (A=025, B=140)
+Fout=898.60 (A=026, B=140)
+Fout=898.70 (A=027, B=140)
+Fout=898.80 (A=028, B=140)
+Fout=898.90 (A=029, B=140)
+Fout=899.00 (A=030, B=140)
+Fout=899.10 (A=031, B=140)
+Fout=899.20 (A=032, B=140)
+Fout=899.30 (A=033, B=140)
+Fout=899.40 (A=034, B=140)
+Fout=899.50 (A=035, B=140)
+Fout=899.60 (A=036, B=140)
+Fout=899.70 (A=037, B=140)
+Fout=899.80 (A=038, B=140)
+Fout=899.90 (A=039, B=140)
+Fout=900.00 (A=040, B=140)
+Fout=900.10 (A=041, B=140)
+Fout=900.20 (A=042, B=140)
+Fout=900.30 (A=043, B=140)
+Fout=900.40 (A=044, B=140)
+Fout=900.50 (A=045, B=140)
+Fout=900.60 (A=046, B=140)
+Fout=900.70 (A=047, B=140)
+Fout=900.80 (A=048, B=140)
+Fout=900.90 (A=049, B=140)
+Fout=901.00 (A=050, B=140)
+Fout=901.10 (A=051, B=140)
+Fout=901.20 (A=052, B=140)
+Fout=901.30 (A=053, B=140)
+Fout=901.40 (A=054, B=140)
+Fout=901.50 (A=055, B=140)
+Fout=901.60 (A=056, B=140)
+Fout=901.70 (A=057, B=140)
+Fout=901.80 (A=058, B=140)
+Fout=901.90 (A=059, B=140)
+Fout=902.00 (A=060, B=140)
+Fout=902.10 (A=061, B=140)
+Fout=902.20 (A=062, B=140)
+Fout=902.40 (A=000, B=141)
+Fout=902.50 (A=001, B=141)
+Fout=902.60 (A=002, B=141)
+Fout=902.70 (A=003, B=141)
+Fout=902.80 (A=004, B=141)
+Fout=902.90 (A=005, B=141)
+Fout=903.00 (A=006, B=141)
+Fout=903.10 (A=007, B=141)
+Fout=903.20 (A=008, B=141)
+Fout=903.30 (A=009, B=141)
+Fout=903.40 (A=010, B=141)
+Fout=903.50 (A=011, B=141)
+Fout=903.60 (A=012, B=141)
+Fout=903.70 (A=013, B=141)
+Fout=903.80 (A=014, B=141)
+Fout=903.90 (A=015, B=141)
+Fout=904.00 (A=016, B=141)
+Fout=904.10 (A=017, B=141)
+Fout=904.20 (A=018, B=141)
+Fout=904.30 (A=019, B=141)
+Fout=904.40 (A=020, B=141)
+Fout=904.50 (A=021, B=141)
+Fout=904.60 (A=022, B=141)
+Fout=904.70 (A=023, B=141)
+Fout=904.80 (A=024, B=141)
+Fout=904.90 (A=025, B=141)
+Fout=905.00 (A=026, B=141)
+Fout=905.10 (A=027, B=141)
+Fout=905.20 (A=028, B=141)
+Fout=905.30 (A=029, B=141)
+Fout=905.40 (A=030, B=141)
+Fout=905.50 (A=031, B=141)
+Fout=905.60 (A=032, B=141)
+Fout=905.70 (A=033, B=141)
+Fout=905.80 (A=034, B=141)
+Fout=905.90 (A=035, B=141)
+Fout=906.00 (A=036, B=141)
+Fout=906.10 (A=037, B=141)
+Fout=906.20 (A=038, B=141)
+Fout=906.30 (A=039, B=141)
+Fout=906.40 (A=040, B=141)
+Fout=906.50 (A=041, B=141)
+Fout=906.60 (A=042, B=141)
+Fout=906.70 (A=043, B=141)
+Fout=906.80 (A=044, B=141)
+Fout=906.90 (A=045, B=141)
+Fout=907.00 (A=046, B=141)
+Fout=907.10 (A=047, B=141)
+Fout=907.20 (A=048, B=141)
+Fout=907.30 (A=049, B=141)
+Fout=907.40 (A=050, B=141)
+Fout=907.50 (A=051, B=141)
+Fout=907.60 (A=052, B=141)
+Fout=907.70 (A=053, B=141)
+Fout=907.80 (A=054, B=141)
+Fout=907.90 (A=055, B=141)
+Fout=908.00 (A=056, B=141)
+Fout=908.10 (A=057, B=141)
+Fout=908.20 (A=058, B=141)
+Fout=908.30 (A=059, B=141)
+Fout=908.40 (A=060, B=141)
+Fout=908.50 (A=061, B=141)
+Fout=908.60 (A=062, B=141)
+Fout=908.80 (A=000, B=142)
+Fout=908.90 (A=001, B=142)
+Fout=909.00 (A=002, B=142)
+Fout=909.10 (A=003, B=142)
+Fout=909.20 (A=004, B=142)
+Fout=909.30 (A=005, B=142)
+Fout=909.40 (A=006, B=142)
+Fout=909.50 (A=007, B=142)
+Fout=909.60 (A=008, B=142)
+Fout=909.70 (A=009, B=142)
+Fout=909.80 (A=010, B=142)
+Fout=909.90 (A=011, B=142)
+Fout=910.00 (A=012, B=142)
+Fout=910.10 (A=013, B=142)
+Fout=910.20 (A=014, B=142)
+Fout=910.30 (A=015, B=142)
+Fout=910.40 (A=016, B=142)
+Fout=910.50 (A=017, B=142)
+Fout=910.60 (A=018, B=142)
+Fout=910.70 (A=019, B=142)
+Fout=910.80 (A=020, B=142)
+Fout=910.90 (A=021, B=142)
+Fout=911.00 (A=022, B=142)
+Fout=911.10 (A=023, B=142)
+Fout=911.20 (A=024, B=142)
+Fout=911.30 (A=025, B=142)
+Fout=911.40 (A=026, B=142)
+Fout=911.50 (A=027, B=142)
+Fout=911.60 (A=028, B=142)
+Fout=911.70 (A=029, B=142)
+Fout=911.80 (A=030, B=142)
+Fout=911.90 (A=031, B=142)
+Fout=912.00 (A=032, B=142)
+Fout=912.10 (A=033, B=142)
+Fout=912.20 (A=034, B=142)
+Fout=912.30 (A=035, B=142)
+Fout=912.40 (A=036, B=142)
+Fout=912.50 (A=037, B=142)
+Fout=912.60 (A=038, B=142)
+Fout=912.70 (A=039, B=142)
+Fout=912.80 (A=040, B=142)
+Fout=912.90 (A=041, B=142)
+Fout=913.00 (A=042, B=142)
+Fout=913.10 (A=043, B=142)
+Fout=913.20 (A=044, B=142)
+Fout=913.30 (A=045, B=142)
+Fout=913.40 (A=046, B=142)
+Fout=913.50 (A=047, B=142)
+Fout=913.60 (A=048, B=142)
+Fout=913.70 (A=049, B=142)
+Fout=913.80 (A=050, B=142)
+Fout=913.90 (A=051, B=142)
+Fout=914.00 (A=052, B=142)
+Fout=914.10 (A=053, B=142)
+Fout=914.20 (A=054, B=142)
+Fout=914.30 (A=055, B=142)
+Fout=914.40 (A=056, B=142)
+Fout=914.50 (A=057, B=142)
+Fout=914.60 (A=058, B=142)
+Fout=914.70 (A=059, B=142)
+Fout=914.80 (A=060, B=142)
+Fout=914.90 (A=061, B=142)
+Fout=915.00 (A=062, B=142)
+Fout=915.20 (A=000, B=143)
+Fout=915.30 (A=001, B=143)
+Fout=915.40 (A=002, B=143)
+Fout=915.50 (A=003, B=143)
+Fout=915.60 (A=004, B=143)
+Fout=915.70 (A=005, B=143)
+Fout=915.80 (A=006, B=143)
+Fout=915.90 (A=007, B=143)
+Fout=916.00 (A=008, B=143)
+Fout=916.10 (A=009, B=143)
+Fout=916.20 (A=010, B=143)
+Fout=916.30 (A=011, B=143)
+Fout=916.40 (A=012, B=143)
+Fout=916.50 (A=013, B=143)
+Fout=916.60 (A=014, B=143)
+Fout=916.70 (A=015, B=143)
+Fout=916.80 (A=016, B=143)
+Fout=916.90 (A=017, B=143)
+Fout=917.00 (A=018, B=143)
+Fout=917.10 (A=019, B=143)
+Fout=917.20 (A=020, B=143)
+Fout=917.30 (A=021, B=143)
+Fout=917.40 (A=022, B=143)
+Fout=917.50 (A=023, B=143)
+Fout=917.60 (A=024, B=143)
+Fout=917.70 (A=025, B=143)
+Fout=917.80 (A=026, B=143)
+Fout=917.90 (A=027, B=143)
+Fout=918.00 (A=028, B=143)
+Fout=918.10 (A=029, B=143)
+Fout=918.20 (A=030, B=143)
+Fout=918.30 (A=031, B=143)
+Fout=918.40 (A=032, B=143)
+Fout=918.50 (A=033, B=143)
+Fout=918.60 (A=034, B=143)
+Fout=918.70 (A=035, B=143)
+Fout=918.80 (A=036, B=143)
+Fout=918.90 (A=037, B=143)
+Fout=919.00 (A=038, B=143)
+Fout=919.10 (A=039, B=143)
+Fout=919.20 (A=040, B=143)
+Fout=919.30 (A=041, B=143)
+Fout=919.40 (A=042, B=143)
+Fout=919.50 (A=043, B=143)
+Fout=919.60 (A=044, B=143)
+Fout=919.70 (A=045, B=143)
+Fout=919.80 (A=046, B=143)
+Fout=919.90 (A=047, B=143)
+Fout=920.00 (A=048, B=143)
+Fout=920.10 (A=049, B=143)
+Fout=920.20 (A=050, B=143)
+Fout=920.30 (A=051, B=143)
+Fout=920.40 (A=052, B=143)
+Fout=920.50 (A=053, B=143)
+Fout=920.60 (A=054, B=143)
+Fout=920.70 (A=055, B=143)
+Fout=920.80 (A=056, B=143)
+Fout=920.90 (A=057, B=143)
+Fout=921.00 (A=058, B=143)
+Fout=921.10 (A=059, B=143)
+Fout=921.20 (A=060, B=143)
+Fout=921.30 (A=061, B=143)
+Fout=921.40 (A=062, B=143)
+Fout=921.60 (A=000, B=144)
+Fout=921.70 (A=001, B=144)
+Fout=921.80 (A=002, B=144)
+Fout=921.90 (A=003, B=144)
+Fout=922.00 (A=004, B=144)
+Fout=922.10 (A=005, B=144)
+Fout=922.20 (A=006, B=144)
+Fout=922.30 (A=007, B=144)
+Fout=922.40 (A=008, B=144)
+Fout=922.50 (A=009, B=144)
+Fout=922.60 (A=010, B=144)
+Fout=922.70 (A=011, B=144)
+Fout=922.80 (A=012, B=144)
+Fout=922.90 (A=013, B=144)
+Fout=923.00 (A=014, B=144)
+Fout=923.10 (A=015, B=144)
+Fout=923.20 (A=016, B=144)
+Fout=923.30 (A=017, B=144)
+Fout=923.40 (A=018, B=144)
+Fout=923.50 (A=019, B=144)
+Fout=923.60 (A=020, B=144)
+Fout=923.70 (A=021, B=144)
+Fout=923.80 (A=022, B=144)
+Fout=923.90 (A=023, B=144)
+Fout=924.00 (A=024, B=144)
+Fout=924.10 (A=025, B=144)
+Fout=924.20 (A=026, B=144)
+Fout=924.30 (A=027, B=144)
+Fout=924.40 (A=028, B=144)
+Fout=924.50 (A=029, B=144)
+Fout=924.60 (A=030, B=144)
+Fout=924.70 (A=031, B=144)
+Fout=924.80 (A=032, B=144)
+Fout=924.90 (A=033, B=144)
+Fout=925.00 (A=034, B=144)
+Fout=925.10 (A=035, B=144)
+Fout=925.20 (A=036, B=144)
+Fout=925.30 (A=037, B=144)
+Fout=925.40 (A=038, B=144)
+Fout=925.50 (A=039, B=144)
+Fout=925.60 (A=040, B=144)
+Fout=925.70 (A=041, B=144)
+Fout=925.80 (A=042, B=144)
+Fout=925.90 (A=043, B=144)
+Fout=926.00 (A=044, B=144)
+Fout=926.10 (A=045, B=144)
+Fout=926.20 (A=046, B=144)
+Fout=926.30 (A=047, B=144)
+Fout=926.40 (A=048, B=144)
+Fout=926.50 (A=049, B=144)
+Fout=926.60 (A=050, B=144)
+Fout=926.70 (A=051, B=144)
+Fout=926.80 (A=052, B=144)
+Fout=926.90 (A=053, B=144)
+Fout=927.00 (A=054, B=144)
+Fout=927.10 (A=055, B=144)
+Fout=927.20 (A=056, B=144)
+Fout=927.30 (A=057, B=144)
+Fout=927.40 (A=058, B=144)
+Fout=927.50 (A=059, B=144)
+Fout=927.60 (A=060, B=144)
+Fout=927.70 (A=061, B=144)
+Fout=927.80 (A=062, B=144)
+Fout=928.00 (A=000, B=145)
+Fout=928.10 (A=001, B=145)
+Fout=928.20 (A=002, B=145)
+Fout=928.30 (A=003, B=145)
+Fout=928.40 (A=004, B=145)
+Fout=928.50 (A=005, B=145)
+Fout=928.60 (A=006, B=145)
+Fout=928.70 (A=007, B=145)
+Fout=928.80 (A=008, B=145)
+Fout=928.90 (A=009, B=145)
+Fout=929.00 (A=010, B=145)
+Fout=929.10 (A=011, B=145)
+Fout=929.20 (A=012, B=145)
+Fout=929.30 (A=013, B=145)
+Fout=929.40 (A=014, B=145)
+Fout=929.50 (A=015, B=145)
+Fout=929.60 (A=016, B=145)
+Fout=929.70 (A=017, B=145)
+Fout=929.80 (A=018, B=145)
+Fout=929.90 (A=019, B=145)
+Fout=930.00 (A=020, B=145)
+Fout=930.10 (A=021, B=145)
+Fout=930.20 (A=022, B=145)
+Fout=930.30 (A=023, B=145)
+Fout=930.40 (A=024, B=145)
+Fout=930.50 (A=025, B=145)
+Fout=930.60 (A=026, B=145)
+Fout=930.70 (A=027, B=145)
+Fout=930.80 (A=028, B=145)
+Fout=930.90 (A=029, B=145)
+Fout=931.00 (A=030, B=145)
+Fout=931.10 (A=031, B=145)
+Fout=931.20 (A=032, B=145)
+Fout=931.30 (A=033, B=145)
+Fout=931.40 (A=034, B=145)
+Fout=931.50 (A=035, B=145)
+Fout=931.60 (A=036, B=145)
+Fout=931.70 (A=037, B=145)
+Fout=931.80 (A=038, B=145)
+Fout=931.90 (A=039, B=145)
+Fout=932.00 (A=040, B=145)
+Fout=932.10 (A=041, B=145)
+Fout=932.20 (A=042, B=145)
+Fout=932.30 (A=043, B=145)
+Fout=932.40 (A=044, B=145)
+Fout=932.50 (A=045, B=145)
+Fout=932.60 (A=046, B=145)
+Fout=932.70 (A=047, B=145)
+Fout=932.80 (A=048, B=145)
+Fout=932.90 (A=049, B=145)
+Fout=933.00 (A=050, B=145)
+Fout=933.10 (A=051, B=145)
+Fout=933.20 (A=052, B=145)
+Fout=933.30 (A=053, B=145)
+Fout=933.40 (A=054, B=145)
+Fout=933.50 (A=055, B=145)
+Fout=933.60 (A=056, B=145)
+Fout=933.70 (A=057, B=145)
+Fout=933.80 (A=058, B=145)
+Fout=933.90 (A=059, B=145)
+Fout=934.00 (A=060, B=145)
+Fout=934.10 (A=061, B=145)
+Fout=934.20 (A=062, B=145)
+Fout=934.40 (A=000, B=146)
+Fout=934.50 (A=001, B=146)
+Fout=934.60 (A=002, B=146)
+Fout=934.70 (A=003, B=146)
+Fout=934.80 (A=004, B=146)
+Fout=934.90 (A=005, B=146)
+Fout=935.00 (A=006, B=146)
+Fout=935.10 (A=007, B=146)
+Fout=935.20 (A=008, B=146)
+Fout=935.30 (A=009, B=146)
+Fout=935.40 (A=010, B=146)
+Fout=935.50 (A=011, B=146)
+Fout=935.60 (A=012, B=146)
+Fout=935.70 (A=013, B=146)
+Fout=935.80 (A=014, B=146)
+Fout=935.90 (A=015, B=146)
+Fout=936.00 (A=016, B=146)
+Fout=936.10 (A=017, B=146)
+Fout=936.20 (A=018, B=146)
+Fout=936.30 (A=019, B=146)
+Fout=936.40 (A=020, B=146)
+Fout=936.50 (A=021, B=146)
+Fout=936.60 (A=022, B=146)
+Fout=936.70 (A=023, B=146)
+Fout=936.80 (A=024, B=146)
+Fout=936.90 (A=025, B=146)
+Fout=937.00 (A=026, B=146)
+Fout=937.10 (A=027, B=146)
+Fout=937.20 (A=028, B=146)
+Fout=937.30 (A=029, B=146)
+Fout=937.40 (A=030, B=146)
+Fout=937.50 (A=031, B=146)
+Fout=937.60 (A=032, B=146)
+Fout=937.70 (A=033, B=146)
+Fout=937.80 (A=034, B=146)
+Fout=937.90 (A=035, B=146)
+Fout=938.00 (A=036, B=146)
+Fout=938.10 (A=037, B=146)
+Fout=938.20 (A=038, B=146)
+Fout=938.30 (A=039, B=146)
+Fout=938.40 (A=040, B=146)
+Fout=938.50 (A=041, B=146)
+Fout=938.60 (A=042, B=146)
+Fout=938.70 (A=043, B=146)
+Fout=938.80 (A=044, B=146)
+Fout=938.90 (A=045, B=146)
+Fout=939.00 (A=046, B=146)
+Fout=939.10 (A=047, B=146)
+Fout=939.20 (A=048, B=146)
+Fout=939.30 (A=049, B=146)
+Fout=939.40 (A=050, B=146)
+Fout=939.50 (A=051, B=146)
+Fout=939.60 (A=052, B=146)
+Fout=939.70 (A=053, B=146)
+Fout=939.80 (A=054, B=146)
+Fout=939.90 (A=055, B=146)
+Fout=940.00 (A=056, B=146)
+Fout=940.10 (A=057, B=146)
+Fout=940.20 (A=058, B=146)
+Fout=940.30 (A=059, B=146)
+Fout=940.40 (A=060, B=146)
+Fout=940.50 (A=061, B=146)
+Fout=940.60 (A=062, B=146)
+Fout=940.80 (A=000, B=147)
+Fout=940.90 (A=001, B=147)
+Fout=941.00 (A=002, B=147)
+Fout=941.10 (A=003, B=147)
+Fout=941.20 (A=004, B=147)
+Fout=941.30 (A=005, B=147)
+Fout=941.40 (A=006, B=147)
+Fout=941.50 (A=007, B=147)
+Fout=941.60 (A=008, B=147)
+Fout=941.70 (A=009, B=147)
+Fout=941.80 (A=010, B=147)
+Fout=941.90 (A=011, B=147)
+Fout=942.00 (A=012, B=147)
+Fout=942.10 (A=013, B=147)
+Fout=942.20 (A=014, B=147)
+Fout=942.30 (A=015, B=147)
+Fout=942.40 (A=016, B=147)
+Fout=942.50 (A=017, B=147)
+Fout=942.60 (A=018, B=147)
+Fout=942.70 (A=019, B=147)
+Fout=942.80 (A=020, B=147)
+Fout=942.90 (A=021, B=147)
+Fout=943.00 (A=022, B=147)
+Fout=943.10 (A=023, B=147)
+Fout=943.20 (A=024, B=147)
+Fout=943.30 (A=025, B=147)
+Fout=943.40 (A=026, B=147)
+Fout=943.50 (A=027, B=147)
+Fout=943.60 (A=028, B=147)
+Fout=943.70 (A=029, B=147)
+Fout=943.80 (A=030, B=147)
+Fout=943.90 (A=031, B=147)
+Fout=944.00 (A=032, B=147)
+Fout=944.10 (A=033, B=147)
+Fout=944.20 (A=034, B=147)
+Fout=944.30 (A=035, B=147)
+Fout=944.40 (A=036, B=147)
+Fout=944.50 (A=037, B=147)
+Fout=944.60 (A=038, B=147)
+Fout=944.70 (A=039, B=147)
+Fout=944.80 (A=040, B=147)
+Fout=944.90 (A=041, B=147)
+Fout=945.00 (A=042, B=147)
+Fout=945.10 (A=043, B=147)
+Fout=945.20 (A=044, B=147)
+Fout=945.30 (A=045, B=147)
+Fout=945.40 (A=046, B=147)
+Fout=945.50 (A=047, B=147)
+Fout=945.60 (A=048, B=147)
+Fout=945.70 (A=049, B=147)
+Fout=945.80 (A=050, B=147)
+Fout=945.90 (A=051, B=147)
+Fout=946.00 (A=052, B=147)
+Fout=946.10 (A=053, B=147)
+Fout=946.20 (A=054, B=147)
+Fout=946.30 (A=055, B=147)
+Fout=946.40 (A=056, B=147)
+Fout=946.50 (A=057, B=147)
+Fout=946.60 (A=058, B=147)
+Fout=946.70 (A=059, B=147)
+Fout=946.80 (A=060, B=147)
+Fout=946.90 (A=061, B=147)
+Fout=947.00 (A=062, B=147)
+Fout=947.20 (A=000, B=148)
+Fout=947.30 (A=001, B=148)
+Fout=947.40 (A=002, B=148)
+Fout=947.50 (A=003, B=148)
+Fout=947.60 (A=004, B=148)
+Fout=947.70 (A=005, B=148)
+Fout=947.80 (A=006, B=148)
+Fout=947.90 (A=007, B=148)
+Fout=948.00 (A=008, B=148)
+Fout=948.10 (A=009, B=148)
+Fout=948.20 (A=010, B=148)
+Fout=948.30 (A=011, B=148)
+Fout=948.40 (A=012, B=148)
+Fout=948.50 (A=013, B=148)
+Fout=948.60 (A=014, B=148)
+Fout=948.70 (A=015, B=148)
+Fout=948.80 (A=016, B=148)
+Fout=948.90 (A=017, B=148)
+Fout=949.00 (A=018, B=148)
+Fout=949.10 (A=019, B=148)
+Fout=949.20 (A=020, B=148)
+Fout=949.30 (A=021, B=148)
+Fout=949.40 (A=022, B=148)
+Fout=949.50 (A=023, B=148)
+Fout=949.60 (A=024, B=148)
+Fout=949.70 (A=025, B=148)
+Fout=949.80 (A=026, B=148)
+Fout=949.90 (A=027, B=148)
+Fout=950.00 (A=028, B=148)
+Fout=950.10 (A=029, B=148)
+Fout=950.20 (A=030, B=148)
+Fout=950.30 (A=031, B=148)
+Fout=950.40 (A=032, B=148)
+Fout=950.50 (A=033, B=148)
+Fout=950.60 (A=034, B=148)
+Fout=950.70 (A=035, B=148)
+Fout=950.80 (A=036, B=148)
+Fout=950.90 (A=037, B=148)
+Fout=951.00 (A=038, B=148)
+Fout=951.10 (A=039, B=148)
+Fout=951.20 (A=040, B=148)
+Fout=951.30 (A=041, B=148)
+Fout=951.40 (A=042, B=148)
+Fout=951.50 (A=043, B=148)
+Fout=951.60 (A=044, B=148)
+Fout=951.70 (A=045, B=148)
+Fout=951.80 (A=046, B=148)
+Fout=951.90 (A=047, B=148)
+Fout=952.00 (A=048, B=148)
+Fout=952.10 (A=049, B=148)
+Fout=952.20 (A=050, B=148)
+Fout=952.30 (A=051, B=148)
+Fout=952.40 (A=052, B=148)
+Fout=952.50 (A=053, B=148)
+Fout=952.60 (A=054, B=148)
+Fout=952.70 (A=055, B=148)
+Fout=952.80 (A=056, B=148)
+Fout=952.90 (A=057, B=148)
+Fout=953.00 (A=058, B=148)
+Fout=953.10 (A=059, B=148)
+Fout=953.20 (A=060, B=148)
+Fout=953.30 (A=061, B=148)
+Fout=953.40 (A=062, B=148)
+Fout=953.60 (A=000, B=149)
+Fout=953.70 (A=001, B=149)
+Fout=953.80 (A=002, B=149)
+Fout=953.90 (A=003, B=149)
+Fout=954.00 (A=004, B=149)
+Fout=954.10 (A=005, B=149)
+Fout=954.20 (A=006, B=149)
+Fout=954.30 (A=007, B=149)
+Fout=954.40 (A=008, B=149)
+Fout=954.50 (A=009, B=149)
+Fout=954.60 (A=010, B=149)
+Fout=954.70 (A=011, B=149)
+Fout=954.80 (A=012, B=149)
+Fout=954.90 (A=013, B=149)
+Fout=955.00 (A=014, B=149)
+Fout=955.10 (A=015, B=149)
+Fout=955.20 (A=016, B=149)
+Fout=955.30 (A=017, B=149)
+Fout=955.40 (A=018, B=149)
+Fout=955.50 (A=019, B=149)
+Fout=955.60 (A=020, B=149)
+Fout=955.70 (A=021, B=149)
+Fout=955.80 (A=022, B=149)
+Fout=955.90 (A=023, B=149)
+Fout=956.00 (A=024, B=149)
+Fout=956.10 (A=025, B=149)
+Fout=956.20 (A=026, B=149)
+Fout=956.30 (A=027, B=149)
+Fout=956.40 (A=028, B=149)
+Fout=956.50 (A=029, B=149)
+Fout=956.60 (A=030, B=149)
+Fout=956.70 (A=031, B=149)
+Fout=956.80 (A=032, B=149)
+Fout=956.90 (A=033, B=149)
+Fout=957.00 (A=034, B=149)
+Fout=957.10 (A=035, B=149)
+Fout=957.20 (A=036, B=149)
+Fout=957.30 (A=037, B=149)
+Fout=957.40 (A=038, B=149)
+Fout=957.50 (A=039, B=149)
+Fout=957.60 (A=040, B=149)
+Fout=957.70 (A=041, B=149)
+Fout=957.80 (A=042, B=149)
+Fout=957.90 (A=043, B=149)
+Fout=958.00 (A=044, B=149)
+Fout=958.10 (A=045, B=149)
+Fout=958.20 (A=046, B=149)
+Fout=958.30 (A=047, B=149)
+Fout=958.40 (A=048, B=149)
+Fout=958.50 (A=049, B=149)
+Fout=958.60 (A=050, B=149)
+Fout=958.70 (A=051, B=149)
+Fout=958.80 (A=052, B=149)
+Fout=958.90 (A=053, B=149)
+Fout=959.00 (A=054, B=149)
+Fout=959.10 (A=055, B=149)
+Fout=959.20 (A=056, B=149)
+Fout=959.30 (A=057, B=149)
+Fout=959.40 (A=058, B=149)
+Fout=959.50 (A=059, B=149)
+Fout=959.60 (A=060, B=149)
+Fout=959.70 (A=061, B=149)
+Fout=959.80 (A=062, B=149)
+Fout=960.00 (A=000, B=150)
+Fout=960.10 (A=001, B=150)
+Fout=960.20 (A=002, B=150)
+Fout=960.30 (A=003, B=150)
+Fout=960.40 (A=004, B=150)
+Fout=960.50 (A=005, B=150)
+Fout=960.60 (A=006, B=150)
+Fout=960.70 (A=007, B=150)
+Fout=960.80 (A=008, B=150)
+Fout=960.90 (A=009, B=150)
+Fout=961.00 (A=010, B=150)
+Fout=961.10 (A=011, B=150)
+Fout=961.20 (A=012, B=150)
+Fout=961.30 (A=013, B=150)
+Fout=961.40 (A=014, B=150)
+Fout=961.50 (A=015, B=150)
+Fout=961.60 (A=016, B=150)
+Fout=961.70 (A=017, B=150)
+Fout=961.80 (A=018, B=150)
+Fout=961.90 (A=019, B=150)
+Fout=962.00 (A=020, B=150)
+Fout=962.10 (A=021, B=150)
+Fout=962.20 (A=022, B=150)
+Fout=962.30 (A=023, B=150)
+Fout=962.40 (A=024, B=150)
+Fout=962.50 (A=025, B=150)
+Fout=962.60 (A=026, B=150)
+Fout=962.70 (A=027, B=150)
+Fout=962.80 (A=028, B=150)
+Fout=962.90 (A=029, B=150)
+Fout=963.00 (A=030, B=150)
+Fout=963.10 (A=031, B=150)
+Fout=963.20 (A=032, B=150)
+Fout=963.30 (A=033, B=150)
+Fout=963.40 (A=034, B=150)
+Fout=963.50 (A=035, B=150)
+Fout=963.60 (A=036, B=150)
+Fout=963.70 (A=037, B=150)
+Fout=963.80 (A=038, B=150)
+Fout=963.90 (A=039, B=150)
+Fout=964.00 (A=040, B=150)
+Fout=964.10 (A=041, B=150)
+Fout=964.20 (A=042, B=150)
+Fout=964.30 (A=043, B=150)
+Fout=964.40 (A=044, B=150)
+Fout=964.50 (A=045, B=150)
+Fout=964.60 (A=046, B=150)
+Fout=964.70 (A=047, B=150)
+Fout=964.80 (A=048, B=150)
+Fout=964.90 (A=049, B=150)
+Fout=965.00 (A=050, B=150)
+Fout=965.10 (A=051, B=150)
+Fout=965.20 (A=052, B=150)
+Fout=965.30 (A=053, B=150)
+Fout=965.40 (A=054, B=150)
+Fout=965.50 (A=055, B=150)
+Fout=965.60 (A=056, B=150)
+Fout=965.70 (A=057, B=150)
+Fout=965.80 (A=058, B=150)
+Fout=965.90 (A=059, B=150)
+Fout=966.00 (A=060, B=150)
+Fout=966.10 (A=061, B=150)
+Fout=966.20 (A=062, B=150)
+======================================================================
+PLL Rx High Band:
+Fout=1804.80 (A=000, B=141)
+Fout=1805.00 (A=001, B=141)
+Fout=1805.20 (A=002, B=141)
+Fout=1805.40 (A=003, B=141)
+Fout=1805.60 (A=004, B=141)
+Fout=1805.80 (A=005, B=141)
+Fout=1806.00 (A=006, B=141)
+Fout=1806.20 (A=007, B=141)
+Fout=1806.40 (A=008, B=141)
+Fout=1806.60 (A=009, B=141)
+Fout=1806.80 (A=010, B=141)
+Fout=1807.00 (A=011, B=141)
+Fout=1807.20 (A=012, B=141)
+Fout=1807.40 (A=013, B=141)
+Fout=1807.60 (A=014, B=141)
+Fout=1807.80 (A=015, B=141)
+Fout=1808.00 (A=016, B=141)
+Fout=1808.20 (A=017, B=141)
+Fout=1808.40 (A=018, B=141)
+Fout=1808.60 (A=019, B=141)
+Fout=1808.80 (A=020, B=141)
+Fout=1809.00 (A=021, B=141)
+Fout=1809.20 (A=022, B=141)
+Fout=1809.40 (A=023, B=141)
+Fout=1809.60 (A=024, B=141)
+Fout=1809.80 (A=025, B=141)
+Fout=1810.00 (A=026, B=141)
+Fout=1810.20 (A=027, B=141)
+Fout=1810.40 (A=028, B=141)
+Fout=1810.60 (A=029, B=141)
+Fout=1810.80 (A=030, B=141)
+Fout=1811.00 (A=031, B=141)
+Fout=1811.20 (A=032, B=141)
+Fout=1811.40 (A=033, B=141)
+Fout=1811.60 (A=034, B=141)
+Fout=1811.80 (A=035, B=141)
+Fout=1812.00 (A=036, B=141)
+Fout=1812.20 (A=037, B=141)
+Fout=1812.40 (A=038, B=141)
+Fout=1812.60 (A=039, B=141)
+Fout=1812.80 (A=040, B=141)
+Fout=1813.00 (A=041, B=141)
+Fout=1813.20 (A=042, B=141)
+Fout=1813.40 (A=043, B=141)
+Fout=1813.60 (A=044, B=141)
+Fout=1813.80 (A=045, B=141)
+Fout=1814.00 (A=046, B=141)
+Fout=1814.20 (A=047, B=141)
+Fout=1814.40 (A=048, B=141)
+Fout=1814.60 (A=049, B=141)
+Fout=1814.80 (A=050, B=141)
+Fout=1815.00 (A=051, B=141)
+Fout=1815.20 (A=052, B=141)
+Fout=1815.40 (A=053, B=141)
+Fout=1815.60 (A=054, B=141)
+Fout=1815.80 (A=055, B=141)
+Fout=1816.00 (A=056, B=141)
+Fout=1816.20 (A=057, B=141)
+Fout=1816.40 (A=058, B=141)
+Fout=1816.60 (A=059, B=141)
+Fout=1816.80 (A=060, B=141)
+Fout=1817.00 (A=061, B=141)
+Fout=1817.20 (A=062, B=141)
+Fout=1817.60 (A=000, B=142)
+Fout=1817.80 (A=001, B=142)
+Fout=1818.00 (A=002, B=142)
+Fout=1818.20 (A=003, B=142)
+Fout=1818.40 (A=004, B=142)
+Fout=1818.60 (A=005, B=142)
+Fout=1818.80 (A=006, B=142)
+Fout=1819.00 (A=007, B=142)
+Fout=1819.20 (A=008, B=142)
+Fout=1819.40 (A=009, B=142)
+Fout=1819.60 (A=010, B=142)
+Fout=1819.80 (A=011, B=142)
+Fout=1820.00 (A=012, B=142)
+Fout=1820.20 (A=013, B=142)
+Fout=1820.40 (A=014, B=142)
+Fout=1820.60 (A=015, B=142)
+Fout=1820.80 (A=016, B=142)
+Fout=1821.00 (A=017, B=142)
+Fout=1821.20 (A=018, B=142)
+Fout=1821.40 (A=019, B=142)
+Fout=1821.60 (A=020, B=142)
+Fout=1821.80 (A=021, B=142)
+Fout=1822.00 (A=022, B=142)
+Fout=1822.20 (A=023, B=142)
+Fout=1822.40 (A=024, B=142)
+Fout=1822.60 (A=025, B=142)
+Fout=1822.80 (A=026, B=142)
+Fout=1823.00 (A=027, B=142)
+Fout=1823.20 (A=028, B=142)
+Fout=1823.40 (A=029, B=142)
+Fout=1823.60 (A=030, B=142)
+Fout=1823.80 (A=031, B=142)
+Fout=1824.00 (A=032, B=142)
+Fout=1824.20 (A=033, B=142)
+Fout=1824.40 (A=034, B=142)
+Fout=1824.60 (A=035, B=142)
+Fout=1824.80 (A=036, B=142)
+Fout=1825.00 (A=037, B=142)
+Fout=1825.20 (A=038, B=142)
+Fout=1825.40 (A=039, B=142)
+Fout=1825.60 (A=040, B=142)
+Fout=1825.80 (A=041, B=142)
+Fout=1826.00 (A=042, B=142)
+Fout=1826.20 (A=043, B=142)
+Fout=1826.40 (A=044, B=142)
+Fout=1826.60 (A=045, B=142)
+Fout=1826.80 (A=046, B=142)
+Fout=1827.00 (A=047, B=142)
+Fout=1827.20 (A=048, B=142)
+Fout=1827.40 (A=049, B=142)
+Fout=1827.60 (A=050, B=142)
+Fout=1827.80 (A=051, B=142)
+Fout=1828.00 (A=052, B=142)
+Fout=1828.20 (A=053, B=142)
+Fout=1828.40 (A=054, B=142)
+Fout=1828.60 (A=055, B=142)
+Fout=1828.80 (A=056, B=142)
+Fout=1829.00 (A=057, B=142)
+Fout=1829.20 (A=058, B=142)
+Fout=1829.40 (A=059, B=142)
+Fout=1829.60 (A=060, B=142)
+Fout=1829.80 (A=061, B=142)
+Fout=1830.00 (A=062, B=142)
+Fout=1830.40 (A=000, B=143)
+Fout=1830.60 (A=001, B=143)
+Fout=1830.80 (A=002, B=143)
+Fout=1831.00 (A=003, B=143)
+Fout=1831.20 (A=004, B=143)
+Fout=1831.40 (A=005, B=143)
+Fout=1831.60 (A=006, B=143)
+Fout=1831.80 (A=007, B=143)
+Fout=1832.00 (A=008, B=143)
+Fout=1832.20 (A=009, B=143)
+Fout=1832.40 (A=010, B=143)
+Fout=1832.60 (A=011, B=143)
+Fout=1832.80 (A=012, B=143)
+Fout=1833.00 (A=013, B=143)
+Fout=1833.20 (A=014, B=143)
+Fout=1833.40 (A=015, B=143)
+Fout=1833.60 (A=016, B=143)
+Fout=1833.80 (A=017, B=143)
+Fout=1834.00 (A=018, B=143)
+Fout=1834.20 (A=019, B=143)
+Fout=1834.40 (A=020, B=143)
+Fout=1834.60 (A=021, B=143)
+Fout=1834.80 (A=022, B=143)
+Fout=1835.00 (A=023, B=143)
+Fout=1835.20 (A=024, B=143)
+Fout=1835.40 (A=025, B=143)
+Fout=1835.60 (A=026, B=143)
+Fout=1835.80 (A=027, B=143)
+Fout=1836.00 (A=028, B=143)
+Fout=1836.20 (A=029, B=143)
+Fout=1836.40 (A=030, B=143)
+Fout=1836.60 (A=031, B=143)
+Fout=1836.80 (A=032, B=143)
+Fout=1837.00 (A=033, B=143)
+Fout=1837.20 (A=034, B=143)
+Fout=1837.40 (A=035, B=143)
+Fout=1837.60 (A=036, B=143)
+Fout=1837.80 (A=037, B=143)
+Fout=1838.00 (A=038, B=143)
+Fout=1838.20 (A=039, B=143)
+Fout=1838.40 (A=040, B=143)
+Fout=1838.60 (A=041, B=143)
+Fout=1838.80 (A=042, B=143)
+Fout=1839.00 (A=043, B=143)
+Fout=1839.20 (A=044, B=143)
+Fout=1839.40 (A=045, B=143)
+Fout=1839.60 (A=046, B=143)
+Fout=1839.80 (A=047, B=143)
+Fout=1840.00 (A=048, B=143)
+Fout=1840.20 (A=049, B=143)
+Fout=1840.40 (A=050, B=143)
+Fout=1840.60 (A=051, B=143)
+Fout=1840.80 (A=052, B=143)
+Fout=1841.00 (A=053, B=143)
+Fout=1841.20 (A=054, B=143)
+Fout=1841.40 (A=055, B=143)
+Fout=1841.60 (A=056, B=143)
+Fout=1841.80 (A=057, B=143)
+Fout=1842.00 (A=058, B=143)
+Fout=1842.20 (A=059, B=143)
+Fout=1842.40 (A=060, B=143)
+Fout=1842.60 (A=061, B=143)
+Fout=1842.80 (A=062, B=143)
+Fout=1843.20 (A=000, B=144)
+Fout=1843.40 (A=001, B=144)
+Fout=1843.60 (A=002, B=144)
+Fout=1843.80 (A=003, B=144)
+Fout=1844.00 (A=004, B=144)
+Fout=1844.20 (A=005, B=144)
+Fout=1844.40 (A=006, B=144)
+Fout=1844.60 (A=007, B=144)
+Fout=1844.80 (A=008, B=144)
+Fout=1845.00 (A=009, B=144)
+Fout=1845.20 (A=010, B=144)
+Fout=1845.40 (A=011, B=144)
+Fout=1845.60 (A=012, B=144)
+Fout=1845.80 (A=013, B=144)
+Fout=1846.00 (A=014, B=144)
+Fout=1846.20 (A=015, B=144)
+Fout=1846.40 (A=016, B=144)
+Fout=1846.60 (A=017, B=144)
+Fout=1846.80 (A=018, B=144)
+Fout=1847.00 (A=019, B=144)
+Fout=1847.20 (A=020, B=144)
+Fout=1847.40 (A=021, B=144)
+Fout=1847.60 (A=022, B=144)
+Fout=1847.80 (A=023, B=144)
+Fout=1848.00 (A=024, B=144)
+Fout=1848.20 (A=025, B=144)
+Fout=1848.40 (A=026, B=144)
+Fout=1848.60 (A=027, B=144)
+Fout=1848.80 (A=028, B=144)
+Fout=1849.00 (A=029, B=144)
+Fout=1849.20 (A=030, B=144)
+Fout=1849.40 (A=031, B=144)
+Fout=1849.60 (A=032, B=144)
+Fout=1849.80 (A=033, B=144)
+Fout=1850.00 (A=034, B=144)
+Fout=1850.20 (A=035, B=144)
+Fout=1850.40 (A=036, B=144)
+Fout=1850.60 (A=037, B=144)
+Fout=1850.80 (A=038, B=144)
+Fout=1851.00 (A=039, B=144)
+Fout=1851.20 (A=040, B=144)
+Fout=1851.40 (A=041, B=144)
+Fout=1851.60 (A=042, B=144)
+Fout=1851.80 (A=043, B=144)
+Fout=1852.00 (A=044, B=144)
+Fout=1852.20 (A=045, B=144)
+Fout=1852.40 (A=046, B=144)
+Fout=1852.60 (A=047, B=144)
+Fout=1852.80 (A=048, B=144)
+Fout=1853.00 (A=049, B=144)
+Fout=1853.20 (A=050, B=144)
+Fout=1853.40 (A=051, B=144)
+Fout=1853.60 (A=052, B=144)
+Fout=1853.80 (A=053, B=144)
+Fout=1854.00 (A=054, B=144)
+Fout=1854.20 (A=055, B=144)
+Fout=1854.40 (A=056, B=144)
+Fout=1854.60 (A=057, B=144)
+Fout=1854.80 (A=058, B=144)
+Fout=1855.00 (A=059, B=144)
+Fout=1855.20 (A=060, B=144)
+Fout=1855.40 (A=061, B=144)
+Fout=1855.60 (A=062, B=144)
+Fout=1856.00 (A=000, B=145)
+Fout=1856.20 (A=001, B=145)
+Fout=1856.40 (A=002, B=145)
+Fout=1856.60 (A=003, B=145)
+Fout=1856.80 (A=004, B=145)
+Fout=1857.00 (A=005, B=145)
+Fout=1857.20 (A=006, B=145)
+Fout=1857.40 (A=007, B=145)
+Fout=1857.60 (A=008, B=145)
+Fout=1857.80 (A=009, B=145)
+Fout=1858.00 (A=010, B=145)
+Fout=1858.20 (A=011, B=145)
+Fout=1858.40 (A=012, B=145)
+Fout=1858.60 (A=013, B=145)
+Fout=1858.80 (A=014, B=145)
+Fout=1859.00 (A=015, B=145)
+Fout=1859.20 (A=016, B=145)
+Fout=1859.40 (A=017, B=145)
+Fout=1859.60 (A=018, B=145)
+Fout=1859.80 (A=019, B=145)
+Fout=1860.00 (A=020, B=145)
+Fout=1860.20 (A=021, B=145)
+Fout=1860.40 (A=022, B=145)
+Fout=1860.60 (A=023, B=145)
+Fout=1860.80 (A=024, B=145)
+Fout=1861.00 (A=025, B=145)
+Fout=1861.20 (A=026, B=145)
+Fout=1861.40 (A=027, B=145)
+Fout=1861.60 (A=028, B=145)
+Fout=1861.80 (A=029, B=145)
+Fout=1862.00 (A=030, B=145)
+Fout=1862.20 (A=031, B=145)
+Fout=1862.40 (A=032, B=145)
+Fout=1862.60 (A=033, B=145)
+Fout=1862.80 (A=034, B=145)
+Fout=1863.00 (A=035, B=145)
+Fout=1863.20 (A=036, B=145)
+Fout=1863.40 (A=037, B=145)
+Fout=1863.60 (A=038, B=145)
+Fout=1863.80 (A=039, B=145)
+Fout=1864.00 (A=040, B=145)
+Fout=1864.20 (A=041, B=145)
+Fout=1864.40 (A=042, B=145)
+Fout=1864.60 (A=043, B=145)
+Fout=1864.80 (A=044, B=145)
+Fout=1865.00 (A=045, B=145)
+Fout=1865.20 (A=046, B=145)
+Fout=1865.40 (A=047, B=145)
+Fout=1865.60 (A=048, B=145)
+Fout=1865.80 (A=049, B=145)
+Fout=1866.00 (A=050, B=145)
+Fout=1866.20 (A=051, B=145)
+Fout=1866.40 (A=052, B=145)
+Fout=1866.60 (A=053, B=145)
+Fout=1866.80 (A=054, B=145)
+Fout=1867.00 (A=055, B=145)
+Fout=1867.20 (A=056, B=145)
+Fout=1867.40 (A=057, B=145)
+Fout=1867.60 (A=058, B=145)
+Fout=1867.80 (A=059, B=145)
+Fout=1868.00 (A=060, B=145)
+Fout=1868.20 (A=061, B=145)
+Fout=1868.40 (A=062, B=145)
+Fout=1868.80 (A=000, B=146)
+Fout=1869.00 (A=001, B=146)
+Fout=1869.20 (A=002, B=146)
+Fout=1869.40 (A=003, B=146)
+Fout=1869.60 (A=004, B=146)
+Fout=1869.80 (A=005, B=146)
+Fout=1870.00 (A=006, B=146)
+Fout=1870.20 (A=007, B=146)
+Fout=1870.40 (A=008, B=146)
+Fout=1870.60 (A=009, B=146)
+Fout=1870.80 (A=010, B=146)
+Fout=1871.00 (A=011, B=146)
+Fout=1871.20 (A=012, B=146)
+Fout=1871.40 (A=013, B=146)
+Fout=1871.60 (A=014, B=146)
+Fout=1871.80 (A=015, B=146)
+Fout=1872.00 (A=016, B=146)
+Fout=1872.20 (A=017, B=146)
+Fout=1872.40 (A=018, B=146)
+Fout=1872.60 (A=019, B=146)
+Fout=1872.80 (A=020, B=146)
+Fout=1873.00 (A=021, B=146)
+Fout=1873.20 (A=022, B=146)
+Fout=1873.40 (A=023, B=146)
+Fout=1873.60 (A=024, B=146)
+Fout=1873.80 (A=025, B=146)
+Fout=1874.00 (A=026, B=146)
+Fout=1874.20 (A=027, B=146)
+Fout=1874.40 (A=028, B=146)
+Fout=1874.60 (A=029, B=146)
+Fout=1874.80 (A=030, B=146)
+Fout=1875.00 (A=031, B=146)
+Fout=1875.20 (A=032, B=146)
+Fout=1875.40 (A=033, B=146)
+Fout=1875.60 (A=034, B=146)
+Fout=1875.80 (A=035, B=146)
+Fout=1876.00 (A=036, B=146)
+Fout=1876.20 (A=037, B=146)
+Fout=1876.40 (A=038, B=146)
+Fout=1876.60 (A=039, B=146)
+Fout=1876.80 (A=040, B=146)
+Fout=1877.00 (A=041, B=146)
+Fout=1877.20 (A=042, B=146)
+Fout=1877.40 (A=043, B=146)
+Fout=1877.60 (A=044, B=146)
+Fout=1877.80 (A=045, B=146)
+Fout=1878.00 (A=046, B=146)
+Fout=1878.20 (A=047, B=146)
+Fout=1878.40 (A=048, B=146)
+Fout=1878.60 (A=049, B=146)
+Fout=1878.80 (A=050, B=146)
+Fout=1879.00 (A=051, B=146)
+Fout=1879.20 (A=052, B=146)
+Fout=1879.40 (A=053, B=146)
+Fout=1879.60 (A=054, B=146)
+Fout=1879.80 (A=055, B=146)
+Fout=1880.00 (A=056, B=146)
+Fout=1880.20 (A=057, B=146)
+Fout=1880.40 (A=058, B=146)
+Fout=1880.60 (A=059, B=146)
+Fout=1880.80 (A=060, B=146)
+Fout=1881.00 (A=061, B=146)
+Fout=1881.20 (A=062, B=146)
+Fout=1881.60 (A=000, B=147)
+Fout=1881.80 (A=001, B=147)
+Fout=1882.00 (A=002, B=147)
+Fout=1882.20 (A=003, B=147)
+Fout=1882.40 (A=004, B=147)
+Fout=1882.60 (A=005, B=147)
+Fout=1882.80 (A=006, B=147)
+Fout=1883.00 (A=007, B=147)
+Fout=1883.20 (A=008, B=147)
+Fout=1883.40 (A=009, B=147)
+Fout=1883.60 (A=010, B=147)
+Fout=1883.80 (A=011, B=147)
+Fout=1884.00 (A=012, B=147)
+Fout=1884.20 (A=013, B=147)
+Fout=1884.40 (A=014, B=147)
+Fout=1884.60 (A=015, B=147)
+Fout=1884.80 (A=016, B=147)
+Fout=1885.00 (A=017, B=147)
+Fout=1885.20 (A=018, B=147)
+Fout=1885.40 (A=019, B=147)
+Fout=1885.60 (A=020, B=147)
+Fout=1885.80 (A=021, B=147)
+Fout=1886.00 (A=022, B=147)
+Fout=1886.20 (A=023, B=147)
+Fout=1886.40 (A=024, B=147)
+Fout=1886.60 (A=025, B=147)
+Fout=1886.80 (A=026, B=147)
+Fout=1887.00 (A=027, B=147)
+Fout=1887.20 (A=028, B=147)
+Fout=1887.40 (A=029, B=147)
+Fout=1887.60 (A=030, B=147)
+Fout=1887.80 (A=031, B=147)
+Fout=1888.00 (A=032, B=147)
+Fout=1888.20 (A=033, B=147)
+Fout=1888.40 (A=034, B=147)
+Fout=1888.60 (A=035, B=147)
+Fout=1888.80 (A=036, B=147)
+Fout=1889.00 (A=037, B=147)
+Fout=1889.20 (A=038, B=147)
+Fout=1889.40 (A=039, B=147)
+Fout=1889.60 (A=040, B=147)
+Fout=1889.80 (A=041, B=147)
+Fout=1890.00 (A=042, B=147)
+Fout=1890.20 (A=043, B=147)
+Fout=1890.40 (A=044, B=147)
+Fout=1890.60 (A=045, B=147)
+Fout=1890.80 (A=046, B=147)
+Fout=1891.00 (A=047, B=147)
+Fout=1891.20 (A=048, B=147)
+Fout=1891.40 (A=049, B=147)
+Fout=1891.60 (A=050, B=147)
+Fout=1891.80 (A=051, B=147)
+Fout=1892.00 (A=052, B=147)
+Fout=1892.20 (A=053, B=147)
+Fout=1892.40 (A=054, B=147)
+Fout=1892.60 (A=055, B=147)
+Fout=1892.80 (A=056, B=147)
+Fout=1893.00 (A=057, B=147)
+Fout=1893.20 (A=058, B=147)
+Fout=1893.40 (A=059, B=147)
+Fout=1893.60 (A=060, B=147)
+Fout=1893.80 (A=061, B=147)
+Fout=1894.00 (A=062, B=147)
+Fout=1894.40 (A=000, B=148)
+Fout=1894.60 (A=001, B=148)
+Fout=1894.80 (A=002, B=148)
+Fout=1895.00 (A=003, B=148)
+Fout=1895.20 (A=004, B=148)
+Fout=1895.40 (A=005, B=148)
+Fout=1895.60 (A=006, B=148)
+Fout=1895.80 (A=007, B=148)
+Fout=1896.00 (A=008, B=148)
+Fout=1896.20 (A=009, B=148)
+Fout=1896.40 (A=010, B=148)
+Fout=1896.60 (A=011, B=148)
+Fout=1896.80 (A=012, B=148)
+Fout=1897.00 (A=013, B=148)
+Fout=1897.20 (A=014, B=148)
+Fout=1897.40 (A=015, B=148)
+Fout=1897.60 (A=016, B=148)
+Fout=1897.80 (A=017, B=148)
+Fout=1898.00 (A=018, B=148)
+Fout=1898.20 (A=019, B=148)
+Fout=1898.40 (A=020, B=148)
+Fout=1898.60 (A=021, B=148)
+Fout=1898.80 (A=022, B=148)
+Fout=1899.00 (A=023, B=148)
+Fout=1899.20 (A=024, B=148)
+Fout=1899.40 (A=025, B=148)
+Fout=1899.60 (A=026, B=148)
+Fout=1899.80 (A=027, B=148)
+Fout=1900.00 (A=028, B=148)
+Fout=1900.20 (A=029, B=148)
+Fout=1900.40 (A=030, B=148)
+Fout=1900.60 (A=031, B=148)
+Fout=1900.80 (A=032, B=148)
+Fout=1901.00 (A=033, B=148)
+Fout=1901.20 (A=034, B=148)
+Fout=1901.40 (A=035, B=148)
+Fout=1901.60 (A=036, B=148)
+Fout=1901.80 (A=037, B=148)
+Fout=1902.00 (A=038, B=148)
+Fout=1902.20 (A=039, B=148)
+Fout=1902.40 (A=040, B=148)
+Fout=1902.60 (A=041, B=148)
+Fout=1902.80 (A=042, B=148)
+Fout=1903.00 (A=043, B=148)
+Fout=1903.20 (A=044, B=148)
+Fout=1903.40 (A=045, B=148)
+Fout=1903.60 (A=046, B=148)
+Fout=1903.80 (A=047, B=148)
+Fout=1904.00 (A=048, B=148)
+Fout=1904.20 (A=049, B=148)
+Fout=1904.40 (A=050, B=148)
+Fout=1904.60 (A=051, B=148)
+Fout=1904.80 (A=052, B=148)
+Fout=1905.00 (A=053, B=148)
+Fout=1905.20 (A=054, B=148)
+Fout=1905.40 (A=055, B=148)
+Fout=1905.60 (A=056, B=148)
+Fout=1905.80 (A=057, B=148)
+Fout=1906.00 (A=058, B=148)
+Fout=1906.20 (A=059, B=148)
+Fout=1906.40 (A=060, B=148)
+Fout=1906.60 (A=061, B=148)
+Fout=1906.80 (A=062, B=148)
+Fout=1907.20 (A=000, B=149)
+Fout=1907.40 (A=001, B=149)
+Fout=1907.60 (A=002, B=149)
+Fout=1907.80 (A=003, B=149)
+Fout=1908.00 (A=004, B=149)
+Fout=1908.20 (A=005, B=149)
+Fout=1908.40 (A=006, B=149)
+Fout=1908.60 (A=007, B=149)
+Fout=1908.80 (A=008, B=149)
+Fout=1909.00 (A=009, B=149)
+Fout=1909.20 (A=010, B=149)
+Fout=1909.40 (A=011, B=149)
+Fout=1909.60 (A=012, B=149)
+Fout=1909.80 (A=013, B=149)
+Fout=1910.00 (A=014, B=149)
+Fout=1910.20 (A=015, B=149)
+Fout=1910.40 (A=016, B=149)
+Fout=1910.60 (A=017, B=149)
+Fout=1910.80 (A=018, B=149)
+Fout=1911.00 (A=019, B=149)
+Fout=1911.20 (A=020, B=149)
+Fout=1911.40 (A=021, B=149)
+Fout=1911.60 (A=022, B=149)
+Fout=1911.80 (A=023, B=149)
+Fout=1912.00 (A=024, B=149)
+Fout=1912.20 (A=025, B=149)
+Fout=1912.40 (A=026, B=149)
+Fout=1912.60 (A=027, B=149)
+Fout=1912.80 (A=028, B=149)
+Fout=1913.00 (A=029, B=149)
+Fout=1913.20 (A=030, B=149)
+Fout=1913.40 (A=031, B=149)
+Fout=1913.60 (A=032, B=149)
+Fout=1913.80 (A=033, B=149)
+Fout=1914.00 (A=034, B=149)
+Fout=1914.20 (A=035, B=149)
+Fout=1914.40 (A=036, B=149)
+Fout=1914.60 (A=037, B=149)
+Fout=1914.80 (A=038, B=149)
+Fout=1915.00 (A=039, B=149)
+Fout=1915.20 (A=040, B=149)
+Fout=1915.40 (A=041, B=149)
+Fout=1915.60 (A=042, B=149)
+Fout=1915.80 (A=043, B=149)
+Fout=1916.00 (A=044, B=149)
+Fout=1916.20 (A=045, B=149)
+Fout=1916.40 (A=046, B=149)
+Fout=1916.60 (A=047, B=149)
+Fout=1916.80 (A=048, B=149)
+Fout=1917.00 (A=049, B=149)
+Fout=1917.20 (A=050, B=149)
+Fout=1917.40 (A=051, B=149)
+Fout=1917.60 (A=052, B=149)
+Fout=1917.80 (A=053, B=149)
+Fout=1918.00 (A=054, B=149)
+Fout=1918.20 (A=055, B=149)
+Fout=1918.40 (A=056, B=149)
+Fout=1918.60 (A=057, B=149)
+Fout=1918.80 (A=058, B=149)
+Fout=1919.00 (A=059, B=149)
+Fout=1919.20 (A=060, B=149)
+Fout=1919.40 (A=061, B=149)
+Fout=1919.60 (A=062, B=149)
+Fout=1920.00 (A=000, B=150)
+Fout=1920.20 (A=001, B=150)
+Fout=1920.40 (A=002, B=150)
+Fout=1920.60 (A=003, B=150)
+Fout=1920.80 (A=004, B=150)
+Fout=1921.00 (A=005, B=150)
+Fout=1921.20 (A=006, B=150)
+Fout=1921.40 (A=007, B=150)
+Fout=1921.60 (A=008, B=150)
+Fout=1921.80 (A=009, B=150)
+Fout=1922.00 (A=010, B=150)
+Fout=1922.20 (A=011, B=150)
+Fout=1922.40 (A=012, B=150)
+Fout=1922.60 (A=013, B=150)
+Fout=1922.80 (A=014, B=150)
+Fout=1923.00 (A=015, B=150)
+Fout=1923.20 (A=016, B=150)
+Fout=1923.40 (A=017, B=150)
+Fout=1923.60 (A=018, B=150)
+Fout=1923.80 (A=019, B=150)
+Fout=1924.00 (A=020, B=150)
+Fout=1924.20 (A=021, B=150)
+Fout=1924.40 (A=022, B=150)
+Fout=1924.60 (A=023, B=150)
+Fout=1924.80 (A=024, B=150)
+Fout=1925.00 (A=025, B=150)
+Fout=1925.20 (A=026, B=150)
+Fout=1925.40 (A=027, B=150)
+Fout=1925.60 (A=028, B=150)
+Fout=1925.80 (A=029, B=150)
+Fout=1926.00 (A=030, B=150)
+Fout=1926.20 (A=031, B=150)
+Fout=1926.40 (A=032, B=150)
+Fout=1926.60 (A=033, B=150)
+Fout=1926.80 (A=034, B=150)
+Fout=1927.00 (A=035, B=150)
+Fout=1927.20 (A=036, B=150)
+Fout=1927.40 (A=037, B=150)
+Fout=1927.60 (A=038, B=150)
+Fout=1927.80 (A=039, B=150)
+Fout=1928.00 (A=040, B=150)
+Fout=1928.20 (A=041, B=150)
+Fout=1928.40 (A=042, B=150)
+Fout=1928.60 (A=043, B=150)
+Fout=1928.80 (A=044, B=150)
+Fout=1929.00 (A=045, B=150)
+Fout=1929.20 (A=046, B=150)
+Fout=1929.40 (A=047, B=150)
+Fout=1929.60 (A=048, B=150)
+Fout=1929.80 (A=049, B=150)
+Fout=1930.00 (A=050, B=150)
+Fout=1930.20 (A=051, B=150)
+Fout=1930.40 (A=052, B=150)
+Fout=1930.60 (A=053, B=150)
+Fout=1930.80 (A=054, B=150)
+Fout=1931.00 (A=055, B=150)
+Fout=1931.20 (A=056, B=150)
+Fout=1931.40 (A=057, B=150)
+Fout=1931.60 (A=058, B=150)
+Fout=1931.80 (A=059, B=150)
+Fout=1932.00 (A=060, B=150)
+Fout=1932.20 (A=061, B=150)
+Fout=1932.40 (A=062, B=150)
+Fout=1932.80 (A=000, B=151)
+Fout=1933.00 (A=001, B=151)
+Fout=1933.20 (A=002, B=151)
+Fout=1933.40 (A=003, B=151)
+Fout=1933.60 (A=004, B=151)
+Fout=1933.80 (A=005, B=151)
+Fout=1934.00 (A=006, B=151)
+Fout=1934.20 (A=007, B=151)
+Fout=1934.40 (A=008, B=151)
+Fout=1934.60 (A=009, B=151)
+Fout=1934.80 (A=010, B=151)
+Fout=1935.00 (A=011, B=151)
+Fout=1935.20 (A=012, B=151)
+Fout=1935.40 (A=013, B=151)
+Fout=1935.60 (A=014, B=151)
+Fout=1935.80 (A=015, B=151)
+Fout=1936.00 (A=016, B=151)
+Fout=1936.20 (A=017, B=151)
+Fout=1936.40 (A=018, B=151)
+Fout=1936.60 (A=019, B=151)
+Fout=1936.80 (A=020, B=151)
+Fout=1937.00 (A=021, B=151)
+Fout=1937.20 (A=022, B=151)
+Fout=1937.40 (A=023, B=151)
+Fout=1937.60 (A=024, B=151)
+Fout=1937.80 (A=025, B=151)
+Fout=1938.00 (A=026, B=151)
+Fout=1938.20 (A=027, B=151)
+Fout=1938.40 (A=028, B=151)
+Fout=1938.60 (A=029, B=151)
+Fout=1938.80 (A=030, B=151)
+Fout=1939.00 (A=031, B=151)
+Fout=1939.20 (A=032, B=151)
+Fout=1939.40 (A=033, B=151)
+Fout=1939.60 (A=034, B=151)
+Fout=1939.80 (A=035, B=151)
+Fout=1940.00 (A=036, B=151)
+Fout=1940.20 (A=037, B=151)
+Fout=1940.40 (A=038, B=151)
+Fout=1940.60 (A=039, B=151)
+Fout=1940.80 (A=040, B=151)
+Fout=1941.00 (A=041, B=151)
+Fout=1941.20 (A=042, B=151)
+Fout=1941.40 (A=043, B=151)
+Fout=1941.60 (A=044, B=151)
+Fout=1941.80 (A=045, B=151)
+Fout=1942.00 (A=046, B=151)
+Fout=1942.20 (A=047, B=151)
+Fout=1942.40 (A=048, B=151)
+Fout=1942.60 (A=049, B=151)
+Fout=1942.80 (A=050, B=151)
+Fout=1943.00 (A=051, B=151)
+Fout=1943.20 (A=052, B=151)
+Fout=1943.40 (A=053, B=151)
+Fout=1943.60 (A=054, B=151)
+Fout=1943.80 (A=055, B=151)
+Fout=1944.00 (A=056, B=151)
+Fout=1944.20 (A=057, B=151)
+Fout=1944.40 (A=058, B=151)
+Fout=1944.60 (A=059, B=151)
+Fout=1944.80 (A=060, B=151)
+Fout=1945.00 (A=061, B=151)
+Fout=1945.20 (A=062, B=151)
+Fout=1945.60 (A=000, B=152)
+Fout=1945.80 (A=001, B=152)
+Fout=1946.00 (A=002, B=152)
+Fout=1946.20 (A=003, B=152)
+Fout=1946.40 (A=004, B=152)
+Fout=1946.60 (A=005, B=152)
+Fout=1946.80 (A=006, B=152)
+Fout=1947.00 (A=007, B=152)
+Fout=1947.20 (A=008, B=152)
+Fout=1947.40 (A=009, B=152)
+Fout=1947.60 (A=010, B=152)
+Fout=1947.80 (A=011, B=152)
+Fout=1948.00 (A=012, B=152)
+Fout=1948.20 (A=013, B=152)
+Fout=1948.40 (A=014, B=152)
+Fout=1948.60 (A=015, B=152)
+Fout=1948.80 (A=016, B=152)
+Fout=1949.00 (A=017, B=152)
+Fout=1949.20 (A=018, B=152)
+Fout=1949.40 (A=019, B=152)
+Fout=1949.60 (A=020, B=152)
+Fout=1949.80 (A=021, B=152)
+Fout=1950.00 (A=022, B=152)
+Fout=1950.20 (A=023, B=152)
+Fout=1950.40 (A=024, B=152)
+Fout=1950.60 (A=025, B=152)
+Fout=1950.80 (A=026, B=152)
+Fout=1951.00 (A=027, B=152)
+Fout=1951.20 (A=028, B=152)
+Fout=1951.40 (A=029, B=152)
+Fout=1951.60 (A=030, B=152)
+Fout=1951.80 (A=031, B=152)
+Fout=1952.00 (A=032, B=152)
+Fout=1952.20 (A=033, B=152)
+Fout=1952.40 (A=034, B=152)
+Fout=1952.60 (A=035, B=152)
+Fout=1952.80 (A=036, B=152)
+Fout=1953.00 (A=037, B=152)
+Fout=1953.20 (A=038, B=152)
+Fout=1953.40 (A=039, B=152)
+Fout=1953.60 (A=040, B=152)
+Fout=1953.80 (A=041, B=152)
+Fout=1954.00 (A=042, B=152)
+Fout=1954.20 (A=043, B=152)
+Fout=1954.40 (A=044, B=152)
+Fout=1954.60 (A=045, B=152)
+Fout=1954.80 (A=046, B=152)
+Fout=1955.00 (A=047, B=152)
+Fout=1955.20 (A=048, B=152)
+Fout=1955.40 (A=049, B=152)
+Fout=1955.60 (A=050, B=152)
+Fout=1955.80 (A=051, B=152)
+Fout=1956.00 (A=052, B=152)
+Fout=1956.20 (A=053, B=152)
+Fout=1956.40 (A=054, B=152)
+Fout=1956.60 (A=055, B=152)
+Fout=1956.80 (A=056, B=152)
+Fout=1957.00 (A=057, B=152)
+Fout=1957.20 (A=058, B=152)
+Fout=1957.40 (A=059, B=152)
+Fout=1957.60 (A=060, B=152)
+Fout=1957.80 (A=061, B=152)
+Fout=1958.00 (A=062, B=152)
+Fout=1958.40 (A=000, B=153)
+Fout=1958.60 (A=001, B=153)
+Fout=1958.80 (A=002, B=153)
+Fout=1959.00 (A=003, B=153)
+Fout=1959.20 (A=004, B=153)
+Fout=1959.40 (A=005, B=153)
+Fout=1959.60 (A=006, B=153)
+Fout=1959.80 (A=007, B=153)
+Fout=1960.00 (A=008, B=153)
+Fout=1960.20 (A=009, B=153)
+Fout=1960.40 (A=010, B=153)
+Fout=1960.60 (A=011, B=153)
+Fout=1960.80 (A=012, B=153)
+Fout=1961.00 (A=013, B=153)
+Fout=1961.20 (A=014, B=153)
+Fout=1961.40 (A=015, B=153)
+Fout=1961.60 (A=016, B=153)
+Fout=1961.80 (A=017, B=153)
+Fout=1962.00 (A=018, B=153)
+Fout=1962.20 (A=019, B=153)
+Fout=1962.40 (A=020, B=153)
+Fout=1962.60 (A=021, B=153)
+Fout=1962.80 (A=022, B=153)
+Fout=1963.00 (A=023, B=153)
+Fout=1963.20 (A=024, B=153)
+Fout=1963.40 (A=025, B=153)
+Fout=1963.60 (A=026, B=153)
+Fout=1963.80 (A=027, B=153)
+Fout=1964.00 (A=028, B=153)
+Fout=1964.20 (A=029, B=153)
+Fout=1964.40 (A=030, B=153)
+Fout=1964.60 (A=031, B=153)
+Fout=1964.80 (A=032, B=153)
+Fout=1965.00 (A=033, B=153)
+Fout=1965.20 (A=034, B=153)
+Fout=1965.40 (A=035, B=153)
+Fout=1965.60 (A=036, B=153)
+Fout=1965.80 (A=037, B=153)
+Fout=1966.00 (A=038, B=153)
+Fout=1966.20 (A=039, B=153)
+Fout=1966.40 (A=040, B=153)
+Fout=1966.60 (A=041, B=153)
+Fout=1966.80 (A=042, B=153)
+Fout=1967.00 (A=043, B=153)
+Fout=1967.20 (A=044, B=153)
+Fout=1967.40 (A=045, B=153)
+Fout=1967.60 (A=046, B=153)
+Fout=1967.80 (A=047, B=153)
+Fout=1968.00 (A=048, B=153)
+Fout=1968.20 (A=049, B=153)
+Fout=1968.40 (A=050, B=153)
+Fout=1968.60 (A=051, B=153)
+Fout=1968.80 (A=052, B=153)
+Fout=1969.00 (A=053, B=153)
+Fout=1969.20 (A=054, B=153)
+Fout=1969.40 (A=055, B=153)
+Fout=1969.60 (A=056, B=153)
+Fout=1969.80 (A=057, B=153)
+Fout=1970.00 (A=058, B=153)
+Fout=1970.20 (A=059, B=153)
+Fout=1970.40 (A=060, B=153)
+Fout=1970.60 (A=061, B=153)
+Fout=1970.80 (A=062, B=153)
+Fout=1971.20 (A=000, B=154)
+Fout=1971.40 (A=001, B=154)
+Fout=1971.60 (A=002, B=154)
+Fout=1971.80 (A=003, B=154)
+Fout=1972.00 (A=004, B=154)
+Fout=1972.20 (A=005, B=154)
+Fout=1972.40 (A=006, B=154)
+Fout=1972.60 (A=007, B=154)
+Fout=1972.80 (A=008, B=154)
+Fout=1973.00 (A=009, B=154)
+Fout=1973.20 (A=010, B=154)
+Fout=1973.40 (A=011, B=154)
+Fout=1973.60 (A=012, B=154)
+Fout=1973.80 (A=013, B=154)
+Fout=1974.00 (A=014, B=154)
+Fout=1974.20 (A=015, B=154)
+Fout=1974.40 (A=016, B=154)
+Fout=1974.60 (A=017, B=154)
+Fout=1974.80 (A=018, B=154)
+Fout=1975.00 (A=019, B=154)
+Fout=1975.20 (A=020, B=154)
+Fout=1975.40 (A=021, B=154)
+Fout=1975.60 (A=022, B=154)
+Fout=1975.80 (A=023, B=154)
+Fout=1976.00 (A=024, B=154)
+Fout=1976.20 (A=025, B=154)
+Fout=1976.40 (A=026, B=154)
+Fout=1976.60 (A=027, B=154)
+Fout=1976.80 (A=028, B=154)
+Fout=1977.00 (A=029, B=154)
+Fout=1977.20 (A=030, B=154)
+Fout=1977.40 (A=031, B=154)
+Fout=1977.60 (A=032, B=154)
+Fout=1977.80 (A=033, B=154)
+Fout=1978.00 (A=034, B=154)
+Fout=1978.20 (A=035, B=154)
+Fout=1978.40 (A=036, B=154)
+Fout=1978.60 (A=037, B=154)
+Fout=1978.80 (A=038, B=154)
+Fout=1979.00 (A=039, B=154)
+Fout=1979.20 (A=040, B=154)
+Fout=1979.40 (A=041, B=154)
+Fout=1979.60 (A=042, B=154)
+Fout=1979.80 (A=043, B=154)
+Fout=1980.00 (A=044, B=154)
+Fout=1980.20 (A=045, B=154)
+Fout=1980.40 (A=046, B=154)
+Fout=1980.60 (A=047, B=154)
+Fout=1980.80 (A=048, B=154)
+Fout=1981.00 (A=049, B=154)
+Fout=1981.20 (A=050, B=154)
+Fout=1981.40 (A=051, B=154)
+Fout=1981.60 (A=052, B=154)
+Fout=1981.80 (A=053, B=154)
+Fout=1982.00 (A=054, B=154)
+Fout=1982.20 (A=055, B=154)
+Fout=1982.40 (A=056, B=154)
+Fout=1982.60 (A=057, B=154)
+Fout=1982.80 (A=058, B=154)
+Fout=1983.00 (A=059, B=154)
+Fout=1983.20 (A=060, B=154)
+Fout=1983.40 (A=061, B=154)
+Fout=1983.60 (A=062, B=154)
+Fout=1984.00 (A=000, B=155)
+Fout=1984.20 (A=001, B=155)
+Fout=1984.40 (A=002, B=155)
+Fout=1984.60 (A=003, B=155)
+Fout=1984.80 (A=004, B=155)
+Fout=1985.00 (A=005, B=155)
+Fout=1985.20 (A=006, B=155)
+Fout=1985.40 (A=007, B=155)
+Fout=1985.60 (A=008, B=155)
+Fout=1985.80 (A=009, B=155)
+Fout=1986.00 (A=010, B=155)
+Fout=1986.20 (A=011, B=155)
+Fout=1986.40 (A=012, B=155)
+Fout=1986.60 (A=013, B=155)
+Fout=1986.80 (A=014, B=155)
+Fout=1987.00 (A=015, B=155)
+Fout=1987.20 (A=016, B=155)
+Fout=1987.40 (A=017, B=155)
+Fout=1987.60 (A=018, B=155)
+Fout=1987.80 (A=019, B=155)
+Fout=1988.00 (A=020, B=155)
+Fout=1988.20 (A=021, B=155)
+Fout=1988.40 (A=022, B=155)
+Fout=1988.60 (A=023, B=155)
+Fout=1988.80 (A=024, B=155)
+Fout=1989.00 (A=025, B=155)
+Fout=1989.20 (A=026, B=155)
+Fout=1989.40 (A=027, B=155)
+Fout=1989.60 (A=028, B=155)
+Fout=1989.80 (A=029, B=155)
+Fout=1990.00 (A=030, B=155)
+Fout=1990.20 (A=031, B=155)
+Fout=1990.40 (A=032, B=155)
+Fout=1990.60 (A=033, B=155)
+Fout=1990.80 (A=034, B=155)
+Fout=1991.00 (A=035, B=155)
+Fout=1991.20 (A=036, B=155)
+Fout=1991.40 (A=037, B=155)
+Fout=1991.60 (A=038, B=155)
+Fout=1991.80 (A=039, B=155)
+Fout=1992.00 (A=040, B=155)
+Fout=1992.20 (A=041, B=155)
+Fout=1992.40 (A=042, B=155)
+Fout=1992.60 (A=043, B=155)
+Fout=1992.80 (A=044, B=155)
+Fout=1993.00 (A=045, B=155)
+Fout=1993.20 (A=046, B=155)
+Fout=1993.40 (A=047, B=155)
+Fout=1993.60 (A=048, B=155)
+Fout=1993.80 (A=049, B=155)
+Fout=1994.00 (A=050, B=155)
+Fout=1994.20 (A=051, B=155)
+Fout=1994.40 (A=052, B=155)
+Fout=1994.60 (A=053, B=155)
+Fout=1994.80 (A=054, B=155)
+Fout=1995.00 (A=055, B=155)
+Fout=1995.20 (A=056, B=155)
+Fout=1995.40 (A=057, B=155)
+Fout=1995.60 (A=058, B=155)
+Fout=1995.80 (A=059, B=155)
+Fout=1996.00 (A=060, B=155)
+Fout=1996.20 (A=061, B=155)
+Fout=1996.40 (A=062, B=155)
+======================================================================
+PLL Tx GSM850_1
+Fout=819.20 (A=000, B=128)
+Fout=819.30 (A=001, B=128)
+Fout=819.40 (A=002, B=128)
+Fout=819.50 (A=003, B=128)
+Fout=819.60 (A=004, B=128)
+Fout=819.70 (A=005, B=128)
+Fout=819.80 (A=006, B=128)
+Fout=819.90 (A=007, B=128)
+Fout=820.00 (A=008, B=128)
+Fout=820.10 (A=009, B=128)
+Fout=820.20 (A=010, B=128)
+Fout=820.30 (A=011, B=128)
+Fout=820.40 (A=012, B=128)
+Fout=820.50 (A=013, B=128)
+Fout=820.60 (A=014, B=128)
+Fout=820.70 (A=015, B=128)
+Fout=820.80 (A=016, B=128)
+Fout=820.90 (A=017, B=128)
+Fout=821.00 (A=018, B=128)
+Fout=821.10 (A=019, B=128)
+Fout=821.20 (A=020, B=128)
+Fout=821.30 (A=021, B=128)
+Fout=821.40 (A=022, B=128)
+Fout=821.50 (A=023, B=128)
+Fout=821.60 (A=024, B=128)
+Fout=821.70 (A=025, B=128)
+Fout=821.80 (A=026, B=128)
+Fout=821.90 (A=027, B=128)
+Fout=822.00 (A=028, B=128)
+Fout=822.10 (A=029, B=128)
+Fout=822.20 (A=030, B=128)
+Fout=822.30 (A=031, B=128)
+Fout=822.40 (A=032, B=128)
+Fout=822.50 (A=033, B=128)
+Fout=822.60 (A=034, B=128)
+Fout=822.70 (A=035, B=128)
+Fout=822.80 (A=036, B=128)
+Fout=822.90 (A=037, B=128)
+Fout=823.00 (A=038, B=128)
+Fout=823.10 (A=039, B=128)
+Fout=823.20 (A=040, B=128)
+Fout=823.30 (A=041, B=128)
+Fout=823.40 (A=042, B=128)
+Fout=823.50 (A=043, B=128)
+Fout=823.60 (A=044, B=128)
+Fout=823.70 (A=045, B=128)
+Fout=823.80 (A=046, B=128)
+Fout=823.90 (A=047, B=128)
+Fout=824.00 (A=048, B=128)
+Fout=824.10 (A=049, B=128)
+Fout=824.20 (A=050, B=128)
+Fout=824.30 (A=051, B=128)
+Fout=824.40 (A=052, B=128)
+Fout=824.50 (A=053, B=128)
+Fout=824.60 (A=054, B=128)
+Fout=824.70 (A=055, B=128)
+Fout=824.80 (A=056, B=128)
+Fout=824.90 (A=057, B=128)
+Fout=825.00 (A=058, B=128)
+Fout=825.10 (A=059, B=128)
+Fout=825.20 (A=060, B=128)
+Fout=825.30 (A=061, B=128)
+Fout=825.40 (A=062, B=128)
+Fout=825.60 (A=000, B=129)
+Fout=825.70 (A=001, B=129)
+Fout=825.80 (A=002, B=129)
+Fout=825.90 (A=003, B=129)
+Fout=826.00 (A=004, B=129)
+Fout=826.10 (A=005, B=129)
+Fout=826.20 (A=006, B=129)
+Fout=826.30 (A=007, B=129)
+Fout=826.40 (A=008, B=129)
+Fout=826.50 (A=009, B=129)
+Fout=826.60 (A=010, B=129)
+Fout=826.70 (A=011, B=129)
+Fout=826.80 (A=012, B=129)
+Fout=826.90 (A=013, B=129)
+Fout=827.00 (A=014, B=129)
+Fout=827.10 (A=015, B=129)
+Fout=827.20 (A=016, B=129)
+Fout=827.30 (A=017, B=129)
+Fout=827.40 (A=018, B=129)
+Fout=827.50 (A=019, B=129)
+Fout=827.60 (A=020, B=129)
+Fout=827.70 (A=021, B=129)
+Fout=827.80 (A=022, B=129)
+Fout=827.90 (A=023, B=129)
+Fout=828.00 (A=024, B=129)
+Fout=828.10 (A=025, B=129)
+Fout=828.20 (A=026, B=129)
+Fout=828.30 (A=027, B=129)
+Fout=828.40 (A=028, B=129)
+Fout=828.50 (A=029, B=129)
+Fout=828.60 (A=030, B=129)
+Fout=828.70 (A=031, B=129)
+Fout=828.80 (A=032, B=129)
+Fout=828.90 (A=033, B=129)
+Fout=829.00 (A=034, B=129)
+Fout=829.10 (A=035, B=129)
+Fout=829.20 (A=036, B=129)
+Fout=829.30 (A=037, B=129)
+Fout=829.40 (A=038, B=129)
+Fout=829.50 (A=039, B=129)
+Fout=829.60 (A=040, B=129)
+Fout=829.70 (A=041, B=129)
+Fout=829.80 (A=042, B=129)
+Fout=829.90 (A=043, B=129)
+Fout=830.00 (A=044, B=129)
+Fout=830.10 (A=045, B=129)
+Fout=830.20 (A=046, B=129)
+Fout=830.30 (A=047, B=129)
+Fout=830.40 (A=048, B=129)
+Fout=830.50 (A=049, B=129)
+Fout=830.60 (A=050, B=129)
+Fout=830.70 (A=051, B=129)
+Fout=830.80 (A=052, B=129)
+Fout=830.90 (A=053, B=129)
+Fout=831.00 (A=054, B=129)
+Fout=831.10 (A=055, B=129)
+Fout=831.20 (A=056, B=129)
+Fout=831.30 (A=057, B=129)
+Fout=831.40 (A=058, B=129)
+Fout=831.50 (A=059, B=129)
+Fout=831.60 (A=060, B=129)
+Fout=831.70 (A=061, B=129)
+Fout=831.80 (A=062, B=129)
+Fout=832.00 (A=000, B=130)
+Fout=832.10 (A=001, B=130)
+Fout=832.20 (A=002, B=130)
+Fout=832.30 (A=003, B=130)
+Fout=832.40 (A=004, B=130)
+Fout=832.50 (A=005, B=130)
+Fout=832.60 (A=006, B=130)
+Fout=832.70 (A=007, B=130)
+Fout=832.80 (A=008, B=130)
+Fout=832.90 (A=009, B=130)
+Fout=833.00 (A=010, B=130)
+Fout=833.10 (A=011, B=130)
+Fout=833.20 (A=012, B=130)
+Fout=833.30 (A=013, B=130)
+Fout=833.40 (A=014, B=130)
+Fout=833.50 (A=015, B=130)
+Fout=833.60 (A=016, B=130)
+Fout=833.70 (A=017, B=130)
+Fout=833.80 (A=018, B=130)
+Fout=833.90 (A=019, B=130)
+Fout=834.00 (A=020, B=130)
+Fout=834.10 (A=021, B=130)
+Fout=834.20 (A=022, B=130)
+Fout=834.30 (A=023, B=130)
+Fout=834.40 (A=024, B=130)
+Fout=834.50 (A=025, B=130)
+Fout=834.60 (A=026, B=130)
+Fout=834.70 (A=027, B=130)
+Fout=834.80 (A=028, B=130)
+Fout=834.90 (A=029, B=130)
+Fout=835.00 (A=030, B=130)
+Fout=835.10 (A=031, B=130)
+Fout=835.20 (A=032, B=130)
+Fout=835.30 (A=033, B=130)
+Fout=835.40 (A=034, B=130)
+Fout=835.50 (A=035, B=130)
+Fout=835.60 (A=036, B=130)
+Fout=835.70 (A=037, B=130)
+Fout=835.80 (A=038, B=130)
+Fout=835.90 (A=039, B=130)
+Fout=836.00 (A=040, B=130)
+Fout=836.10 (A=041, B=130)
+Fout=836.20 (A=042, B=130)
+Fout=836.30 (A=043, B=130)
+Fout=836.40 (A=044, B=130)
+Fout=836.50 (A=045, B=130)
+Fout=836.60 (A=046, B=130)
+Fout=836.70 (A=047, B=130)
+Fout=836.80 (A=048, B=130)
+Fout=836.90 (A=049, B=130)
+Fout=837.00 (A=050, B=130)
+Fout=837.10 (A=051, B=130)
+Fout=837.20 (A=052, B=130)
+Fout=837.30 (A=053, B=130)
+Fout=837.40 (A=054, B=130)
+Fout=837.50 (A=055, B=130)
+Fout=837.60 (A=056, B=130)
+Fout=837.70 (A=057, B=130)
+Fout=837.80 (A=058, B=130)
+Fout=837.90 (A=059, B=130)
+Fout=838.00 (A=060, B=130)
+Fout=838.10 (A=061, B=130)
+Fout=838.20 (A=062, B=130)
+======================================================================
+PLL Tx GSM850_2
+Fout=832.00 (A=000, B=065)
+Fout=832.20 (A=001, B=065)
+Fout=832.40 (A=002, B=065)
+Fout=832.60 (A=003, B=065)
+Fout=832.80 (A=004, B=065)
+Fout=833.00 (A=005, B=065)
+Fout=833.20 (A=006, B=065)
+Fout=833.40 (A=007, B=065)
+Fout=833.60 (A=008, B=065)
+Fout=833.80 (A=009, B=065)
+Fout=834.00 (A=010, B=065)
+Fout=834.20 (A=011, B=065)
+Fout=834.40 (A=012, B=065)
+Fout=834.60 (A=013, B=065)
+Fout=834.80 (A=014, B=065)
+Fout=835.00 (A=015, B=065)
+Fout=835.20 (A=016, B=065)
+Fout=835.40 (A=017, B=065)
+Fout=835.60 (A=018, B=065)
+Fout=835.80 (A=019, B=065)
+Fout=836.00 (A=020, B=065)
+Fout=836.20 (A=021, B=065)
+Fout=836.40 (A=022, B=065)
+Fout=836.60 (A=023, B=065)
+Fout=836.80 (A=024, B=065)
+Fout=837.00 (A=025, B=065)
+Fout=837.20 (A=026, B=065)
+Fout=837.40 (A=027, B=065)
+Fout=837.60 (A=028, B=065)
+Fout=837.80 (A=029, B=065)
+Fout=838.00 (A=030, B=065)
+Fout=838.20 (A=031, B=065)
+Fout=838.40 (A=032, B=065)
+Fout=838.60 (A=033, B=065)
+Fout=838.80 (A=034, B=065)
+Fout=839.00 (A=035, B=065)
+Fout=839.20 (A=036, B=065)
+Fout=839.40 (A=037, B=065)
+Fout=839.60 (A=038, B=065)
+Fout=839.80 (A=039, B=065)
+Fout=840.00 (A=040, B=065)
+Fout=840.20 (A=041, B=065)
+Fout=840.40 (A=042, B=065)
+Fout=840.60 (A=043, B=065)
+Fout=840.80 (A=044, B=065)
+Fout=841.00 (A=045, B=065)
+Fout=841.20 (A=046, B=065)
+Fout=841.40 (A=047, B=065)
+Fout=841.60 (A=048, B=065)
+Fout=841.80 (A=049, B=065)
+Fout=842.00 (A=050, B=065)
+Fout=842.20 (A=051, B=065)
+Fout=842.40 (A=052, B=065)
+Fout=842.60 (A=053, B=065)
+Fout=842.80 (A=054, B=065)
+Fout=843.00 (A=055, B=065)
+Fout=843.20 (A=056, B=065)
+Fout=843.40 (A=057, B=065)
+Fout=843.60 (A=058, B=065)
+Fout=843.80 (A=059, B=065)
+Fout=844.00 (A=060, B=065)
+Fout=844.20 (A=061, B=065)
+Fout=844.40 (A=062, B=065)
+Fout=844.60 (A=063, B=065)
+Fout=844.80 (A=000, B=066)
+Fout=845.00 (A=001, B=066)
+Fout=845.20 (A=002, B=066)
+Fout=845.40 (A=003, B=066)
+Fout=845.60 (A=004, B=066)
+Fout=845.80 (A=005, B=066)
+Fout=846.00 (A=006, B=066)
+Fout=846.20 (A=007, B=066)
+Fout=846.40 (A=008, B=066)
+Fout=846.60 (A=009, B=066)
+Fout=846.80 (A=010, B=066)
+Fout=847.00 (A=011, B=066)
+Fout=847.20 (A=012, B=066)
+Fout=847.40 (A=013, B=066)
+Fout=847.60 (A=014, B=066)
+Fout=847.80 (A=015, B=066)
+Fout=848.00 (A=016, B=066)
+Fout=848.20 (A=017, B=066)
+Fout=848.40 (A=018, B=066)
+Fout=848.60 (A=019, B=066)
+Fout=848.80 (A=020, B=066)
+Fout=849.00 (A=021, B=066)
+Fout=849.20 (A=022, B=066)
+Fout=849.40 (A=023, B=066)
+Fout=849.60 (A=024, B=066)
+Fout=849.80 (A=025, B=066)
+Fout=850.00 (A=026, B=066)
+Fout=850.20 (A=027, B=066)
+Fout=850.40 (A=028, B=066)
+Fout=850.60 (A=029, B=066)
+Fout=850.80 (A=030, B=066)
+Fout=851.00 (A=031, B=066)
+Fout=851.20 (A=032, B=066)
+Fout=851.40 (A=033, B=066)
+Fout=851.60 (A=034, B=066)
+Fout=851.80 (A=035, B=066)
+Fout=852.00 (A=036, B=066)
+Fout=852.20 (A=037, B=066)
+Fout=852.40 (A=038, B=066)
+Fout=852.60 (A=039, B=066)
+Fout=852.80 (A=040, B=066)
+Fout=853.00 (A=041, B=066)
+Fout=853.20 (A=042, B=066)
+Fout=853.40 (A=043, B=066)
+Fout=853.60 (A=044, B=066)
+Fout=853.80 (A=045, B=066)
+Fout=854.00 (A=046, B=066)
+Fout=854.20 (A=047, B=066)
+Fout=854.40 (A=048, B=066)
+Fout=854.60 (A=049, B=066)
+Fout=854.80 (A=050, B=066)
+Fout=855.00 (A=051, B=066)
+Fout=855.20 (A=052, B=066)
+Fout=855.40 (A=053, B=066)
+Fout=855.60 (A=054, B=066)
+Fout=855.80 (A=055, B=066)
+Fout=856.00 (A=056, B=066)
+Fout=856.20 (A=057, B=066)
+Fout=856.40 (A=058, B=066)
+Fout=856.60 (A=059, B=066)
+Fout=856.80 (A=060, B=066)
+Fout=857.00 (A=061, B=066)
+Fout=857.20 (A=062, B=066)
+Fout=857.40 (A=063, B=066)
+======================================================================
+PLL Tx GSM900
+Fout=870.40 (A=000, B=068)
+Fout=870.60 (A=001, B=068)
+Fout=870.80 (A=002, B=068)
+Fout=871.00 (A=003, B=068)
+Fout=871.20 (A=004, B=068)
+Fout=871.40 (A=005, B=068)
+Fout=871.60 (A=006, B=068)
+Fout=871.80 (A=007, B=068)
+Fout=872.00 (A=008, B=068)
+Fout=872.20 (A=009, B=068)
+Fout=872.40 (A=010, B=068)
+Fout=872.60 (A=011, B=068)
+Fout=872.80 (A=012, B=068)
+Fout=873.00 (A=013, B=068)
+Fout=873.20 (A=014, B=068)
+Fout=873.40 (A=015, B=068)
+Fout=873.60 (A=016, B=068)
+Fout=873.80 (A=017, B=068)
+Fout=874.00 (A=018, B=068)
+Fout=874.20 (A=019, B=068)
+Fout=874.40 (A=020, B=068)
+Fout=874.60 (A=021, B=068)
+Fout=874.80 (A=022, B=068)
+Fout=875.00 (A=023, B=068)
+Fout=875.20 (A=024, B=068)
+Fout=875.40 (A=025, B=068)
+Fout=875.60 (A=026, B=068)
+Fout=875.80 (A=027, B=068)
+Fout=876.00 (A=028, B=068)
+Fout=876.20 (A=029, B=068)
+Fout=876.40 (A=030, B=068)
+Fout=876.60 (A=031, B=068)
+Fout=876.80 (A=032, B=068)
+Fout=877.00 (A=033, B=068)
+Fout=877.20 (A=034, B=068)
+Fout=877.40 (A=035, B=068)
+Fout=877.60 (A=036, B=068)
+Fout=877.80 (A=037, B=068)
+Fout=878.00 (A=038, B=068)
+Fout=878.20 (A=039, B=068)
+Fout=878.40 (A=040, B=068)
+Fout=878.60 (A=041, B=068)
+Fout=878.80 (A=042, B=068)
+Fout=879.00 (A=043, B=068)
+Fout=879.20 (A=044, B=068)
+Fout=879.40 (A=045, B=068)
+Fout=879.60 (A=046, B=068)
+Fout=879.80 (A=047, B=068)
+Fout=880.00 (A=048, B=068)
+Fout=880.20 (A=049, B=068)
+Fout=880.40 (A=050, B=068)
+Fout=880.60 (A=051, B=068)
+Fout=880.80 (A=052, B=068)
+Fout=881.00 (A=053, B=068)
+Fout=881.20 (A=054, B=068)
+Fout=881.40 (A=055, B=068)
+Fout=881.60 (A=056, B=068)
+Fout=881.80 (A=057, B=068)
+Fout=882.00 (A=058, B=068)
+Fout=882.20 (A=059, B=068)
+Fout=882.40 (A=060, B=068)
+Fout=882.60 (A=061, B=068)
+Fout=882.80 (A=062, B=068)
+Fout=883.00 (A=063, B=068)
+Fout=883.20 (A=000, B=069)
+Fout=883.40 (A=001, B=069)
+Fout=883.60 (A=002, B=069)
+Fout=883.80 (A=003, B=069)
+Fout=884.00 (A=004, B=069)
+Fout=884.20 (A=005, B=069)
+Fout=884.40 (A=006, B=069)
+Fout=884.60 (A=007, B=069)
+Fout=884.80 (A=008, B=069)
+Fout=885.00 (A=009, B=069)
+Fout=885.20 (A=010, B=069)
+Fout=885.40 (A=011, B=069)
+Fout=885.60 (A=012, B=069)
+Fout=885.80 (A=013, B=069)
+Fout=886.00 (A=014, B=069)
+Fout=886.20 (A=015, B=069)
+Fout=886.40 (A=016, B=069)
+Fout=886.60 (A=017, B=069)
+Fout=886.80 (A=018, B=069)
+Fout=887.00 (A=019, B=069)
+Fout=887.20 (A=020, B=069)
+Fout=887.40 (A=021, B=069)
+Fout=887.60 (A=022, B=069)
+Fout=887.80 (A=023, B=069)
+Fout=888.00 (A=024, B=069)
+Fout=888.20 (A=025, B=069)
+Fout=888.40 (A=026, B=069)
+Fout=888.60 (A=027, B=069)
+Fout=888.80 (A=028, B=069)
+Fout=889.00 (A=029, B=069)
+Fout=889.20 (A=030, B=069)
+Fout=889.40 (A=031, B=069)
+Fout=889.60 (A=032, B=069)
+Fout=889.80 (A=033, B=069)
+Fout=890.00 (A=034, B=069)
+Fout=890.20 (A=035, B=069)
+Fout=890.40 (A=036, B=069)
+Fout=890.60 (A=037, B=069)
+Fout=890.80 (A=038, B=069)
+Fout=891.00 (A=039, B=069)
+Fout=891.20 (A=040, B=069)
+Fout=891.40 (A=041, B=069)
+Fout=891.60 (A=042, B=069)
+Fout=891.80 (A=043, B=069)
+Fout=892.00 (A=044, B=069)
+Fout=892.20 (A=045, B=069)
+Fout=892.40 (A=046, B=069)
+Fout=892.60 (A=047, B=069)
+Fout=892.80 (A=048, B=069)
+Fout=893.00 (A=049, B=069)
+Fout=893.20 (A=050, B=069)
+Fout=893.40 (A=051, B=069)
+Fout=893.60 (A=052, B=069)
+Fout=893.80 (A=053, B=069)
+Fout=894.00 (A=054, B=069)
+Fout=894.20 (A=055, B=069)
+Fout=894.40 (A=056, B=069)
+Fout=894.60 (A=057, B=069)
+Fout=894.80 (A=058, B=069)
+Fout=895.00 (A=059, B=069)
+Fout=895.20 (A=060, B=069)
+Fout=895.40 (A=061, B=069)
+Fout=895.60 (A=062, B=069)
+Fout=895.80 (A=063, B=069)
+Fout=896.00 (A=000, B=070)
+Fout=896.20 (A=001, B=070)
+Fout=896.40 (A=002, B=070)
+Fout=896.60 (A=003, B=070)
+Fout=896.80 (A=004, B=070)
+Fout=897.00 (A=005, B=070)
+Fout=897.20 (A=006, B=070)
+Fout=897.40 (A=007, B=070)
+Fout=897.60 (A=008, B=070)
+Fout=897.80 (A=009, B=070)
+Fout=898.00 (A=010, B=070)
+Fout=898.20 (A=011, B=070)
+Fout=898.40 (A=012, B=070)
+Fout=898.60 (A=013, B=070)
+Fout=898.80 (A=014, B=070)
+Fout=899.00 (A=015, B=070)
+Fout=899.20 (A=016, B=070)
+Fout=899.40 (A=017, B=070)
+Fout=899.60 (A=018, B=070)
+Fout=899.80 (A=019, B=070)
+Fout=900.00 (A=020, B=070)
+Fout=900.20 (A=021, B=070)
+Fout=900.40 (A=022, B=070)
+Fout=900.60 (A=023, B=070)
+Fout=900.80 (A=024, B=070)
+Fout=901.00 (A=025, B=070)
+Fout=901.20 (A=026, B=070)
+Fout=901.40 (A=027, B=070)
+Fout=901.60 (A=028, B=070)
+Fout=901.80 (A=029, B=070)
+Fout=902.00 (A=030, B=070)
+Fout=902.20 (A=031, B=070)
+Fout=902.40 (A=032, B=070)
+Fout=902.60 (A=033, B=070)
+Fout=902.80 (A=034, B=070)
+Fout=903.00 (A=035, B=070)
+Fout=903.20 (A=036, B=070)
+Fout=903.40 (A=037, B=070)
+Fout=903.60 (A=038, B=070)
+Fout=903.80 (A=039, B=070)
+Fout=904.00 (A=040, B=070)
+Fout=904.20 (A=041, B=070)
+Fout=904.40 (A=042, B=070)
+Fout=904.60 (A=043, B=070)
+Fout=904.80 (A=044, B=070)
+Fout=905.00 (A=045, B=070)
+Fout=905.20 (A=046, B=070)
+Fout=905.40 (A=047, B=070)
+Fout=905.60 (A=048, B=070)
+Fout=905.80 (A=049, B=070)
+Fout=906.00 (A=050, B=070)
+Fout=906.20 (A=051, B=070)
+Fout=906.40 (A=052, B=070)
+Fout=906.60 (A=053, B=070)
+Fout=906.80 (A=054, B=070)
+Fout=907.00 (A=055, B=070)
+Fout=907.20 (A=056, B=070)
+Fout=907.40 (A=057, B=070)
+Fout=907.60 (A=058, B=070)
+Fout=907.80 (A=059, B=070)
+Fout=908.00 (A=060, B=070)
+Fout=908.20 (A=061, B=070)
+Fout=908.40 (A=062, B=070)
+Fout=908.60 (A=063, B=070)
+Fout=908.80 (A=000, B=071)
+Fout=909.00 (A=001, B=071)
+Fout=909.20 (A=002, B=071)
+Fout=909.40 (A=003, B=071)
+Fout=909.60 (A=004, B=071)
+Fout=909.80 (A=005, B=071)
+Fout=910.00 (A=006, B=071)
+Fout=910.20 (A=007, B=071)
+Fout=910.40 (A=008, B=071)
+Fout=910.60 (A=009, B=071)
+Fout=910.80 (A=010, B=071)
+Fout=911.00 (A=011, B=071)
+Fout=911.20 (A=012, B=071)
+Fout=911.40 (A=013, B=071)
+Fout=911.60 (A=014, B=071)
+Fout=911.80 (A=015, B=071)
+Fout=912.00 (A=016, B=071)
+Fout=912.20 (A=017, B=071)
+Fout=912.40 (A=018, B=071)
+Fout=912.60 (A=019, B=071)
+Fout=912.80 (A=020, B=071)
+Fout=913.00 (A=021, B=071)
+Fout=913.20 (A=022, B=071)
+Fout=913.40 (A=023, B=071)
+Fout=913.60 (A=024, B=071)
+Fout=913.80 (A=025, B=071)
+Fout=914.00 (A=026, B=071)
+Fout=914.20 (A=027, B=071)
+Fout=914.40 (A=028, B=071)
+Fout=914.60 (A=029, B=071)
+Fout=914.80 (A=030, B=071)
+Fout=915.00 (A=031, B=071)
+Fout=915.20 (A=032, B=071)
+Fout=915.40 (A=033, B=071)
+Fout=915.60 (A=034, B=071)
+Fout=915.80 (A=035, B=071)
+Fout=916.00 (A=036, B=071)
+Fout=916.20 (A=037, B=071)
+Fout=916.40 (A=038, B=071)
+Fout=916.60 (A=039, B=071)
+Fout=916.80 (A=040, B=071)
+Fout=917.00 (A=041, B=071)
+Fout=917.20 (A=042, B=071)
+Fout=917.40 (A=043, B=071)
+Fout=917.60 (A=044, B=071)
+Fout=917.80 (A=045, B=071)
+Fout=918.00 (A=046, B=071)
+Fout=918.20 (A=047, B=071)
+Fout=918.40 (A=048, B=071)
+Fout=918.60 (A=049, B=071)
+Fout=918.80 (A=050, B=071)
+Fout=919.00 (A=051, B=071)
+Fout=919.20 (A=052, B=071)
+Fout=919.40 (A=053, B=071)
+Fout=919.60 (A=054, B=071)
+Fout=919.80 (A=055, B=071)
+Fout=920.00 (A=056, B=071)
+Fout=920.20 (A=057, B=071)
+Fout=920.40 (A=058, B=071)
+Fout=920.60 (A=059, B=071)
+Fout=920.80 (A=060, B=071)
+Fout=921.00 (A=061, B=071)
+Fout=921.20 (A=062, B=071)
+Fout=921.40 (A=063, B=071)
+======================================================================
+PLL Tx GSM1800/1900
+Fout=1702.40 (A=000, B=133)
+Fout=1702.60 (A=001, B=133)
+Fout=1702.80 (A=002, B=133)
+Fout=1703.00 (A=003, B=133)
+Fout=1703.20 (A=004, B=133)
+Fout=1703.40 (A=005, B=133)
+Fout=1703.60 (A=006, B=133)
+Fout=1703.80 (A=007, B=133)
+Fout=1704.00 (A=008, B=133)
+Fout=1704.20 (A=009, B=133)
+Fout=1704.40 (A=010, B=133)
+Fout=1704.60 (A=011, B=133)
+Fout=1704.80 (A=012, B=133)
+Fout=1705.00 (A=013, B=133)
+Fout=1705.20 (A=014, B=133)
+Fout=1705.40 (A=015, B=133)
+Fout=1705.60 (A=016, B=133)
+Fout=1705.80 (A=017, B=133)
+Fout=1706.00 (A=018, B=133)
+Fout=1706.20 (A=019, B=133)
+Fout=1706.40 (A=020, B=133)
+Fout=1706.60 (A=021, B=133)
+Fout=1706.80 (A=022, B=133)
+Fout=1707.00 (A=023, B=133)
+Fout=1707.20 (A=024, B=133)
+Fout=1707.40 (A=025, B=133)
+Fout=1707.60 (A=026, B=133)
+Fout=1707.80 (A=027, B=133)
+Fout=1708.00 (A=028, B=133)
+Fout=1708.20 (A=029, B=133)
+Fout=1708.40 (A=030, B=133)
+Fout=1708.60 (A=031, B=133)
+Fout=1708.80 (A=032, B=133)
+Fout=1709.00 (A=033, B=133)
+Fout=1709.20 (A=034, B=133)
+Fout=1709.40 (A=035, B=133)
+Fout=1709.60 (A=036, B=133)
+Fout=1709.80 (A=037, B=133)
+Fout=1710.00 (A=038, B=133)
+Fout=1710.20 (A=039, B=133)
+Fout=1710.40 (A=040, B=133)
+Fout=1710.60 (A=041, B=133)
+Fout=1710.80 (A=042, B=133)
+Fout=1711.00 (A=043, B=133)
+Fout=1711.20 (A=044, B=133)
+Fout=1711.40 (A=045, B=133)
+Fout=1711.60 (A=046, B=133)
+Fout=1711.80 (A=047, B=133)
+Fout=1712.00 (A=048, B=133)
+Fout=1712.20 (A=049, B=133)
+Fout=1712.40 (A=050, B=133)
+Fout=1712.60 (A=051, B=133)
+Fout=1712.80 (A=052, B=133)
+Fout=1713.00 (A=053, B=133)
+Fout=1713.20 (A=054, B=133)
+Fout=1713.40 (A=055, B=133)
+Fout=1713.60 (A=056, B=133)
+Fout=1713.80 (A=057, B=133)
+Fout=1714.00 (A=058, B=133)
+Fout=1714.20 (A=059, B=133)
+Fout=1714.40 (A=060, B=133)
+Fout=1714.60 (A=061, B=133)
+Fout=1714.80 (A=062, B=133)
+Fout=1715.00 (A=063, B=133)
+Fout=1715.20 (A=000, B=134)
+Fout=1715.40 (A=001, B=134)
+Fout=1715.60 (A=002, B=134)
+Fout=1715.80 (A=003, B=134)
+Fout=1716.00 (A=004, B=134)
+Fout=1716.20 (A=005, B=134)
+Fout=1716.40 (A=006, B=134)
+Fout=1716.60 (A=007, B=134)
+Fout=1716.80 (A=008, B=134)
+Fout=1717.00 (A=009, B=134)
+Fout=1717.20 (A=010, B=134)
+Fout=1717.40 (A=011, B=134)
+Fout=1717.60 (A=012, B=134)
+Fout=1717.80 (A=013, B=134)
+Fout=1718.00 (A=014, B=134)
+Fout=1718.20 (A=015, B=134)
+Fout=1718.40 (A=016, B=134)
+Fout=1718.60 (A=017, B=134)
+Fout=1718.80 (A=018, B=134)
+Fout=1719.00 (A=019, B=134)
+Fout=1719.20 (A=020, B=134)
+Fout=1719.40 (A=021, B=134)
+Fout=1719.60 (A=022, B=134)
+Fout=1719.80 (A=023, B=134)
+Fout=1720.00 (A=024, B=134)
+Fout=1720.20 (A=025, B=134)
+Fout=1720.40 (A=026, B=134)
+Fout=1720.60 (A=027, B=134)
+Fout=1720.80 (A=028, B=134)
+Fout=1721.00 (A=029, B=134)
+Fout=1721.20 (A=030, B=134)
+Fout=1721.40 (A=031, B=134)
+Fout=1721.60 (A=032, B=134)
+Fout=1721.80 (A=033, B=134)
+Fout=1722.00 (A=034, B=134)
+Fout=1722.20 (A=035, B=134)
+Fout=1722.40 (A=036, B=134)
+Fout=1722.60 (A=037, B=134)
+Fout=1722.80 (A=038, B=134)
+Fout=1723.00 (A=039, B=134)
+Fout=1723.20 (A=040, B=134)
+Fout=1723.40 (A=041, B=134)
+Fout=1723.60 (A=042, B=134)
+Fout=1723.80 (A=043, B=134)
+Fout=1724.00 (A=044, B=134)
+Fout=1724.20 (A=045, B=134)
+Fout=1724.40 (A=046, B=134)
+Fout=1724.60 (A=047, B=134)
+Fout=1724.80 (A=048, B=134)
+Fout=1725.00 (A=049, B=134)
+Fout=1725.20 (A=050, B=134)
+Fout=1725.40 (A=051, B=134)
+Fout=1725.60 (A=052, B=134)
+Fout=1725.80 (A=053, B=134)
+Fout=1726.00 (A=054, B=134)
+Fout=1726.20 (A=055, B=134)
+Fout=1726.40 (A=056, B=134)
+Fout=1726.60 (A=057, B=134)
+Fout=1726.80 (A=058, B=134)
+Fout=1727.00 (A=059, B=134)
+Fout=1727.20 (A=060, B=134)
+Fout=1727.40 (A=061, B=134)
+Fout=1727.60 (A=062, B=134)
+Fout=1727.80 (A=063, B=134)
+Fout=1728.00 (A=000, B=135)
+Fout=1728.20 (A=001, B=135)
+Fout=1728.40 (A=002, B=135)
+Fout=1728.60 (A=003, B=135)
+Fout=1728.80 (A=004, B=135)
+Fout=1729.00 (A=005, B=135)
+Fout=1729.20 (A=006, B=135)
+Fout=1729.40 (A=007, B=135)
+Fout=1729.60 (A=008, B=135)
+Fout=1729.80 (A=009, B=135)
+Fout=1730.00 (A=010, B=135)
+Fout=1730.20 (A=011, B=135)
+Fout=1730.40 (A=012, B=135)
+Fout=1730.60 (A=013, B=135)
+Fout=1730.80 (A=014, B=135)
+Fout=1731.00 (A=015, B=135)
+Fout=1731.20 (A=016, B=135)
+Fout=1731.40 (A=017, B=135)
+Fout=1731.60 (A=018, B=135)
+Fout=1731.80 (A=019, B=135)
+Fout=1732.00 (A=020, B=135)
+Fout=1732.20 (A=021, B=135)
+Fout=1732.40 (A=022, B=135)
+Fout=1732.60 (A=023, B=135)
+Fout=1732.80 (A=024, B=135)
+Fout=1733.00 (A=025, B=135)
+Fout=1733.20 (A=026, B=135)
+Fout=1733.40 (A=027, B=135)
+Fout=1733.60 (A=028, B=135)
+Fout=1733.80 (A=029, B=135)
+Fout=1734.00 (A=030, B=135)
+Fout=1734.20 (A=031, B=135)
+Fout=1734.40 (A=032, B=135)
+Fout=1734.60 (A=033, B=135)
+Fout=1734.80 (A=034, B=135)
+Fout=1735.00 (A=035, B=135)
+Fout=1735.20 (A=036, B=135)
+Fout=1735.40 (A=037, B=135)
+Fout=1735.60 (A=038, B=135)
+Fout=1735.80 (A=039, B=135)
+Fout=1736.00 (A=040, B=135)
+Fout=1736.20 (A=041, B=135)
+Fout=1736.40 (A=042, B=135)
+Fout=1736.60 (A=043, B=135)
+Fout=1736.80 (A=044, B=135)
+Fout=1737.00 (A=045, B=135)
+Fout=1737.20 (A=046, B=135)
+Fout=1737.40 (A=047, B=135)
+Fout=1737.60 (A=048, B=135)
+Fout=1737.80 (A=049, B=135)
+Fout=1738.00 (A=050, B=135)
+Fout=1738.20 (A=051, B=135)
+Fout=1738.40 (A=052, B=135)
+Fout=1738.60 (A=053, B=135)
+Fout=1738.80 (A=054, B=135)
+Fout=1739.00 (A=055, B=135)
+Fout=1739.20 (A=056, B=135)
+Fout=1739.40 (A=057, B=135)
+Fout=1739.60 (A=058, B=135)
+Fout=1739.80 (A=059, B=135)
+Fout=1740.00 (A=060, B=135)
+Fout=1740.20 (A=061, B=135)
+Fout=1740.40 (A=062, B=135)
+Fout=1740.60 (A=063, B=135)
+Fout=1740.80 (A=000, B=136)
+Fout=1741.00 (A=001, B=136)
+Fout=1741.20 (A=002, B=136)
+Fout=1741.40 (A=003, B=136)
+Fout=1741.60 (A=004, B=136)
+Fout=1741.80 (A=005, B=136)
+Fout=1742.00 (A=006, B=136)
+Fout=1742.20 (A=007, B=136)
+Fout=1742.40 (A=008, B=136)
+Fout=1742.60 (A=009, B=136)
+Fout=1742.80 (A=010, B=136)
+Fout=1743.00 (A=011, B=136)
+Fout=1743.20 (A=012, B=136)
+Fout=1743.40 (A=013, B=136)
+Fout=1743.60 (A=014, B=136)
+Fout=1743.80 (A=015, B=136)
+Fout=1744.00 (A=016, B=136)
+Fout=1744.20 (A=017, B=136)
+Fout=1744.40 (A=018, B=136)
+Fout=1744.60 (A=019, B=136)
+Fout=1744.80 (A=020, B=136)
+Fout=1745.00 (A=021, B=136)
+Fout=1745.20 (A=022, B=136)
+Fout=1745.40 (A=023, B=136)
+Fout=1745.60 (A=024, B=136)
+Fout=1745.80 (A=025, B=136)
+Fout=1746.00 (A=026, B=136)
+Fout=1746.20 (A=027, B=136)
+Fout=1746.40 (A=028, B=136)
+Fout=1746.60 (A=029, B=136)
+Fout=1746.80 (A=030, B=136)
+Fout=1747.00 (A=031, B=136)
+Fout=1747.20 (A=032, B=136)
+Fout=1747.40 (A=033, B=136)
+Fout=1747.60 (A=034, B=136)
+Fout=1747.80 (A=035, B=136)
+Fout=1748.00 (A=036, B=136)
+Fout=1748.20 (A=037, B=136)
+Fout=1748.40 (A=038, B=136)
+Fout=1748.60 (A=039, B=136)
+Fout=1748.80 (A=040, B=136)
+Fout=1749.00 (A=041, B=136)
+Fout=1749.20 (A=042, B=136)
+Fout=1749.40 (A=043, B=136)
+Fout=1749.60 (A=044, B=136)
+Fout=1749.80 (A=045, B=136)
+Fout=1750.00 (A=046, B=136)
+Fout=1750.20 (A=047, B=136)
+Fout=1750.40 (A=048, B=136)
+Fout=1750.60 (A=049, B=136)
+Fout=1750.80 (A=050, B=136)
+Fout=1751.00 (A=051, B=136)
+Fout=1751.20 (A=052, B=136)
+Fout=1751.40 (A=053, B=136)
+Fout=1751.60 (A=054, B=136)
+Fout=1751.80 (A=055, B=136)
+Fout=1752.00 (A=056, B=136)
+Fout=1752.20 (A=057, B=136)
+Fout=1752.40 (A=058, B=136)
+Fout=1752.60 (A=059, B=136)
+Fout=1752.80 (A=060, B=136)
+Fout=1753.00 (A=061, B=136)
+Fout=1753.20 (A=062, B=136)
+Fout=1753.40 (A=063, B=136)
+Fout=1753.60 (A=000, B=137)
+Fout=1753.80 (A=001, B=137)
+Fout=1754.00 (A=002, B=137)
+Fout=1754.20 (A=003, B=137)
+Fout=1754.40 (A=004, B=137)
+Fout=1754.60 (A=005, B=137)
+Fout=1754.80 (A=006, B=137)
+Fout=1755.00 (A=007, B=137)
+Fout=1755.20 (A=008, B=137)
+Fout=1755.40 (A=009, B=137)
+Fout=1755.60 (A=010, B=137)
+Fout=1755.80 (A=011, B=137)
+Fout=1756.00 (A=012, B=137)
+Fout=1756.20 (A=013, B=137)
+Fout=1756.40 (A=014, B=137)
+Fout=1756.60 (A=015, B=137)
+Fout=1756.80 (A=016, B=137)
+Fout=1757.00 (A=017, B=137)
+Fout=1757.20 (A=018, B=137)
+Fout=1757.40 (A=019, B=137)
+Fout=1757.60 (A=020, B=137)
+Fout=1757.80 (A=021, B=137)
+Fout=1758.00 (A=022, B=137)
+Fout=1758.20 (A=023, B=137)
+Fout=1758.40 (A=024, B=137)
+Fout=1758.60 (A=025, B=137)
+Fout=1758.80 (A=026, B=137)
+Fout=1759.00 (A=027, B=137)
+Fout=1759.20 (A=028, B=137)
+Fout=1759.40 (A=029, B=137)
+Fout=1759.60 (A=030, B=137)
+Fout=1759.80 (A=031, B=137)
+Fout=1760.00 (A=032, B=137)
+Fout=1760.20 (A=033, B=137)
+Fout=1760.40 (A=034, B=137)
+Fout=1760.60 (A=035, B=137)
+Fout=1760.80 (A=036, B=137)
+Fout=1761.00 (A=037, B=137)
+Fout=1761.20 (A=038, B=137)
+Fout=1761.40 (A=039, B=137)
+Fout=1761.60 (A=040, B=137)
+Fout=1761.80 (A=041, B=137)
+Fout=1762.00 (A=042, B=137)
+Fout=1762.20 (A=043, B=137)
+Fout=1762.40 (A=044, B=137)
+Fout=1762.60 (A=045, B=137)
+Fout=1762.80 (A=046, B=137)
+Fout=1763.00 (A=047, B=137)
+Fout=1763.20 (A=048, B=137)
+Fout=1763.40 (A=049, B=137)
+Fout=1763.60 (A=050, B=137)
+Fout=1763.80 (A=051, B=137)
+Fout=1764.00 (A=052, B=137)
+Fout=1764.20 (A=053, B=137)
+Fout=1764.40 (A=054, B=137)
+Fout=1764.60 (A=055, B=137)
+Fout=1764.80 (A=056, B=137)
+Fout=1765.00 (A=057, B=137)
+Fout=1765.20 (A=058, B=137)
+Fout=1765.40 (A=059, B=137)
+Fout=1765.60 (A=060, B=137)
+Fout=1765.80 (A=061, B=137)
+Fout=1766.00 (A=062, B=137)
+Fout=1766.20 (A=063, B=137)
+Fout=1766.40 (A=000, B=138)
+Fout=1766.60 (A=001, B=138)
+Fout=1766.80 (A=002, B=138)
+Fout=1767.00 (A=003, B=138)
+Fout=1767.20 (A=004, B=138)
+Fout=1767.40 (A=005, B=138)
+Fout=1767.60 (A=006, B=138)
+Fout=1767.80 (A=007, B=138)
+Fout=1768.00 (A=008, B=138)
+Fout=1768.20 (A=009, B=138)
+Fout=1768.40 (A=010, B=138)
+Fout=1768.60 (A=011, B=138)
+Fout=1768.80 (A=012, B=138)
+Fout=1769.00 (A=013, B=138)
+Fout=1769.20 (A=014, B=138)
+Fout=1769.40 (A=015, B=138)
+Fout=1769.60 (A=016, B=138)
+Fout=1769.80 (A=017, B=138)
+Fout=1770.00 (A=018, B=138)
+Fout=1770.20 (A=019, B=138)
+Fout=1770.40 (A=020, B=138)
+Fout=1770.60 (A=021, B=138)
+Fout=1770.80 (A=022, B=138)
+Fout=1771.00 (A=023, B=138)
+Fout=1771.20 (A=024, B=138)
+Fout=1771.40 (A=025, B=138)
+Fout=1771.60 (A=026, B=138)
+Fout=1771.80 (A=027, B=138)
+Fout=1772.00 (A=028, B=138)
+Fout=1772.20 (A=029, B=138)
+Fout=1772.40 (A=030, B=138)
+Fout=1772.60 (A=031, B=138)
+Fout=1772.80 (A=032, B=138)
+Fout=1773.00 (A=033, B=138)
+Fout=1773.20 (A=034, B=138)
+Fout=1773.40 (A=035, B=138)
+Fout=1773.60 (A=036, B=138)
+Fout=1773.80 (A=037, B=138)
+Fout=1774.00 (A=038, B=138)
+Fout=1774.20 (A=039, B=138)
+Fout=1774.40 (A=040, B=138)
+Fout=1774.60 (A=041, B=138)
+Fout=1774.80 (A=042, B=138)
+Fout=1775.00 (A=043, B=138)
+Fout=1775.20 (A=044, B=138)
+Fout=1775.40 (A=045, B=138)
+Fout=1775.60 (A=046, B=138)
+Fout=1775.80 (A=047, B=138)
+Fout=1776.00 (A=048, B=138)
+Fout=1776.20 (A=049, B=138)
+Fout=1776.40 (A=050, B=138)
+Fout=1776.60 (A=051, B=138)
+Fout=1776.80 (A=052, B=138)
+Fout=1777.00 (A=053, B=138)
+Fout=1777.20 (A=054, B=138)
+Fout=1777.40 (A=055, B=138)
+Fout=1777.60 (A=056, B=138)
+Fout=1777.80 (A=057, B=138)
+Fout=1778.00 (A=058, B=138)
+Fout=1778.20 (A=059, B=138)
+Fout=1778.40 (A=060, B=138)
+Fout=1778.60 (A=061, B=138)
+Fout=1778.80 (A=062, B=138)
+Fout=1779.00 (A=063, B=138)
+Fout=1779.20 (A=000, B=139)
+Fout=1779.40 (A=001, B=139)
+Fout=1779.60 (A=002, B=139)
+Fout=1779.80 (A=003, B=139)
+Fout=1780.00 (A=004, B=139)
+Fout=1780.20 (A=005, B=139)
+Fout=1780.40 (A=006, B=139)
+Fout=1780.60 (A=007, B=139)
+Fout=1780.80 (A=008, B=139)
+Fout=1781.00 (A=009, B=139)
+Fout=1781.20 (A=010, B=139)
+Fout=1781.40 (A=011, B=139)
+Fout=1781.60 (A=012, B=139)
+Fout=1781.80 (A=013, B=139)
+Fout=1782.00 (A=014, B=139)
+Fout=1782.20 (A=015, B=139)
+Fout=1782.40 (A=016, B=139)
+Fout=1782.60 (A=017, B=139)
+Fout=1782.80 (A=018, B=139)
+Fout=1783.00 (A=019, B=139)
+Fout=1783.20 (A=020, B=139)
+Fout=1783.40 (A=021, B=139)
+Fout=1783.60 (A=022, B=139)
+Fout=1783.80 (A=023, B=139)
+Fout=1784.00 (A=024, B=139)
+Fout=1784.20 (A=025, B=139)
+Fout=1784.40 (A=026, B=139)
+Fout=1784.60 (A=027, B=139)
+Fout=1784.80 (A=028, B=139)
+Fout=1785.00 (A=029, B=139)
+Fout=1785.20 (A=030, B=139)
+Fout=1785.40 (A=031, B=139)
+Fout=1785.60 (A=032, B=139)
+Fout=1785.80 (A=033, B=139)
+Fout=1786.00 (A=034, B=139)
+Fout=1786.20 (A=035, B=139)
+Fout=1786.40 (A=036, B=139)
+Fout=1786.60 (A=037, B=139)
+Fout=1786.80 (A=038, B=139)
+Fout=1787.00 (A=039, B=139)
+Fout=1787.20 (A=040, B=139)
+Fout=1787.40 (A=041, B=139)
+Fout=1787.60 (A=042, B=139)
+Fout=1787.80 (A=043, B=139)
+Fout=1788.00 (A=044, B=139)
+Fout=1788.20 (A=045, B=139)
+Fout=1788.40 (A=046, B=139)
+Fout=1788.60 (A=047, B=139)
+Fout=1788.80 (A=048, B=139)
+Fout=1789.00 (A=049, B=139)
+Fout=1789.20 (A=050, B=139)
+Fout=1789.40 (A=051, B=139)
+Fout=1789.60 (A=052, B=139)
+Fout=1789.80 (A=053, B=139)
+Fout=1790.00 (A=054, B=139)
+Fout=1790.20 (A=055, B=139)
+Fout=1790.40 (A=056, B=139)
+Fout=1790.60 (A=057, B=139)
+Fout=1790.80 (A=058, B=139)
+Fout=1791.00 (A=059, B=139)
+Fout=1791.20 (A=060, B=139)
+Fout=1791.40 (A=061, B=139)
+Fout=1791.60 (A=062, B=139)
+Fout=1791.80 (A=063, B=139)
+Fout=1792.00 (A=000, B=140)
+Fout=1792.20 (A=001, B=140)
+Fout=1792.40 (A=002, B=140)
+Fout=1792.60 (A=003, B=140)
+Fout=1792.80 (A=004, B=140)
+Fout=1793.00 (A=005, B=140)
+Fout=1793.20 (A=006, B=140)
+Fout=1793.40 (A=007, B=140)
+Fout=1793.60 (A=008, B=140)
+Fout=1793.80 (A=009, B=140)
+Fout=1794.00 (A=010, B=140)
+Fout=1794.20 (A=011, B=140)
+Fout=1794.40 (A=012, B=140)
+Fout=1794.60 (A=013, B=140)
+Fout=1794.80 (A=014, B=140)
+Fout=1795.00 (A=015, B=140)
+Fout=1795.20 (A=016, B=140)
+Fout=1795.40 (A=017, B=140)
+Fout=1795.60 (A=018, B=140)
+Fout=1795.80 (A=019, B=140)
+Fout=1796.00 (A=020, B=140)
+Fout=1796.20 (A=021, B=140)
+Fout=1796.40 (A=022, B=140)
+Fout=1796.60 (A=023, B=140)
+Fout=1796.80 (A=024, B=140)
+Fout=1797.00 (A=025, B=140)
+Fout=1797.20 (A=026, B=140)
+Fout=1797.40 (A=027, B=140)
+Fout=1797.60 (A=028, B=140)
+Fout=1797.80 (A=029, B=140)
+Fout=1798.00 (A=030, B=140)
+Fout=1798.20 (A=031, B=140)
+Fout=1798.40 (A=032, B=140)
+Fout=1798.60 (A=033, B=140)
+Fout=1798.80 (A=034, B=140)
+Fout=1799.00 (A=035, B=140)
+Fout=1799.20 (A=036, B=140)
+Fout=1799.40 (A=037, B=140)
+Fout=1799.60 (A=038, B=140)
+Fout=1799.80 (A=039, B=140)
+Fout=1800.00 (A=040, B=140)
+Fout=1800.20 (A=041, B=140)
+Fout=1800.40 (A=042, B=140)
+Fout=1800.60 (A=043, B=140)
+Fout=1800.80 (A=044, B=140)
+Fout=1801.00 (A=045, B=140)
+Fout=1801.20 (A=046, B=140)
+Fout=1801.40 (A=047, B=140)
+Fout=1801.60 (A=048, B=140)
+Fout=1801.80 (A=049, B=140)
+Fout=1802.00 (A=050, B=140)
+Fout=1802.20 (A=051, B=140)
+Fout=1802.40 (A=052, B=140)
+Fout=1802.60 (A=053, B=140)
+Fout=1802.80 (A=054, B=140)
+Fout=1803.00 (A=055, B=140)
+Fout=1803.20 (A=056, B=140)
+Fout=1803.40 (A=057, B=140)
+Fout=1803.60 (A=058, B=140)
+Fout=1803.80 (A=059, B=140)
+Fout=1804.00 (A=060, B=140)
+Fout=1804.20 (A=061, B=140)
+Fout=1804.40 (A=062, B=140)
+Fout=1804.60 (A=063, B=140)
+Fout=1804.80 (A=000, B=141)
+Fout=1805.00 (A=001, B=141)
+Fout=1805.20 (A=002, B=141)
+Fout=1805.40 (A=003, B=141)
+Fout=1805.60 (A=004, B=141)
+Fout=1805.80 (A=005, B=141)
+Fout=1806.00 (A=006, B=141)
+Fout=1806.20 (A=007, B=141)
+Fout=1806.40 (A=008, B=141)
+Fout=1806.60 (A=009, B=141)
+Fout=1806.80 (A=010, B=141)
+Fout=1807.00 (A=011, B=141)
+Fout=1807.20 (A=012, B=141)
+Fout=1807.40 (A=013, B=141)
+Fout=1807.60 (A=014, B=141)
+Fout=1807.80 (A=015, B=141)
+Fout=1808.00 (A=016, B=141)
+Fout=1808.20 (A=017, B=141)
+Fout=1808.40 (A=018, B=141)
+Fout=1808.60 (A=019, B=141)
+Fout=1808.80 (A=020, B=141)
+Fout=1809.00 (A=021, B=141)
+Fout=1809.20 (A=022, B=141)
+Fout=1809.40 (A=023, B=141)
+Fout=1809.60 (A=024, B=141)
+Fout=1809.80 (A=025, B=141)
+Fout=1810.00 (A=026, B=141)
+Fout=1810.20 (A=027, B=141)
+Fout=1810.40 (A=028, B=141)
+Fout=1810.60 (A=029, B=141)
+Fout=1810.80 (A=030, B=141)
+Fout=1811.00 (A=031, B=141)
+Fout=1811.20 (A=032, B=141)
+Fout=1811.40 (A=033, B=141)
+Fout=1811.60 (A=034, B=141)
+Fout=1811.80 (A=035, B=141)
+Fout=1812.00 (A=036, B=141)
+Fout=1812.20 (A=037, B=141)
+Fout=1812.40 (A=038, B=141)
+Fout=1812.60 (A=039, B=141)
+Fout=1812.80 (A=040, B=141)
+Fout=1813.00 (A=041, B=141)
+Fout=1813.20 (A=042, B=141)
+Fout=1813.40 (A=043, B=141)
+Fout=1813.60 (A=044, B=141)
+Fout=1813.80 (A=045, B=141)
+Fout=1814.00 (A=046, B=141)
+Fout=1814.20 (A=047, B=141)
+Fout=1814.40 (A=048, B=141)
+Fout=1814.60 (A=049, B=141)
+Fout=1814.80 (A=050, B=141)
+Fout=1815.00 (A=051, B=141)
+Fout=1815.20 (A=052, B=141)
+Fout=1815.40 (A=053, B=141)
+Fout=1815.60 (A=054, B=141)
+Fout=1815.80 (A=055, B=141)
+Fout=1816.00 (A=056, B=141)
+Fout=1816.20 (A=057, B=141)
+Fout=1816.40 (A=058, B=141)
+Fout=1816.60 (A=059, B=141)
+Fout=1816.80 (A=060, B=141)
+Fout=1817.00 (A=061, B=141)
+Fout=1817.20 (A=062, B=141)
+Fout=1817.40 (A=063, B=141)
+Fout=1817.60 (A=000, B=142)
+Fout=1817.80 (A=001, B=142)
+Fout=1818.00 (A=002, B=142)
+Fout=1818.20 (A=003, B=142)
+Fout=1818.40 (A=004, B=142)
+Fout=1818.60 (A=005, B=142)
+Fout=1818.80 (A=006, B=142)
+Fout=1819.00 (A=007, B=142)
+Fout=1819.20 (A=008, B=142)
+Fout=1819.40 (A=009, B=142)
+Fout=1819.60 (A=010, B=142)
+Fout=1819.80 (A=011, B=142)
+Fout=1820.00 (A=012, B=142)
+Fout=1820.20 (A=013, B=142)
+Fout=1820.40 (A=014, B=142)
+Fout=1820.60 (A=015, B=142)
+Fout=1820.80 (A=016, B=142)
+Fout=1821.00 (A=017, B=142)
+Fout=1821.20 (A=018, B=142)
+Fout=1821.40 (A=019, B=142)
+Fout=1821.60 (A=020, B=142)
+Fout=1821.80 (A=021, B=142)
+Fout=1822.00 (A=022, B=142)
+Fout=1822.20 (A=023, B=142)
+Fout=1822.40 (A=024, B=142)
+Fout=1822.60 (A=025, B=142)
+Fout=1822.80 (A=026, B=142)
+Fout=1823.00 (A=027, B=142)
+Fout=1823.20 (A=028, B=142)
+Fout=1823.40 (A=029, B=142)
+Fout=1823.60 (A=030, B=142)
+Fout=1823.80 (A=031, B=142)
+Fout=1824.00 (A=032, B=142)
+Fout=1824.20 (A=033, B=142)
+Fout=1824.40 (A=034, B=142)
+Fout=1824.60 (A=035, B=142)
+Fout=1824.80 (A=036, B=142)
+Fout=1825.00 (A=037, B=142)
+Fout=1825.20 (A=038, B=142)
+Fout=1825.40 (A=039, B=142)
+Fout=1825.60 (A=040, B=142)
+Fout=1825.80 (A=041, B=142)
+Fout=1826.00 (A=042, B=142)
+Fout=1826.20 (A=043, B=142)
+Fout=1826.40 (A=044, B=142)
+Fout=1826.60 (A=045, B=142)
+Fout=1826.80 (A=046, B=142)
+Fout=1827.00 (A=047, B=142)
+Fout=1827.20 (A=048, B=142)
+Fout=1827.40 (A=049, B=142)
+Fout=1827.60 (A=050, B=142)
+Fout=1827.80 (A=051, B=142)
+Fout=1828.00 (A=052, B=142)
+Fout=1828.20 (A=053, B=142)
+Fout=1828.40 (A=054, B=142)
+Fout=1828.60 (A=055, B=142)
+Fout=1828.80 (A=056, B=142)
+Fout=1829.00 (A=057, B=142)
+Fout=1829.20 (A=058, B=142)
+Fout=1829.40 (A=059, B=142)
+Fout=1829.60 (A=060, B=142)
+Fout=1829.80 (A=061, B=142)
+Fout=1830.00 (A=062, B=142)
+Fout=1830.20 (A=063, B=142)
+Fout=1830.40 (A=000, B=143)
+Fout=1830.60 (A=001, B=143)
+Fout=1830.80 (A=002, B=143)
+Fout=1831.00 (A=003, B=143)
+Fout=1831.20 (A=004, B=143)
+Fout=1831.40 (A=005, B=143)
+Fout=1831.60 (A=006, B=143)
+Fout=1831.80 (A=007, B=143)
+Fout=1832.00 (A=008, B=143)
+Fout=1832.20 (A=009, B=143)
+Fout=1832.40 (A=010, B=143)
+Fout=1832.60 (A=011, B=143)
+Fout=1832.80 (A=012, B=143)
+Fout=1833.00 (A=013, B=143)
+Fout=1833.20 (A=014, B=143)
+Fout=1833.40 (A=015, B=143)
+Fout=1833.60 (A=016, B=143)
+Fout=1833.80 (A=017, B=143)
+Fout=1834.00 (A=018, B=143)
+Fout=1834.20 (A=019, B=143)
+Fout=1834.40 (A=020, B=143)
+Fout=1834.60 (A=021, B=143)
+Fout=1834.80 (A=022, B=143)
+Fout=1835.00 (A=023, B=143)
+Fout=1835.20 (A=024, B=143)
+Fout=1835.40 (A=025, B=143)
+Fout=1835.60 (A=026, B=143)
+Fout=1835.80 (A=027, B=143)
+Fout=1836.00 (A=028, B=143)
+Fout=1836.20 (A=029, B=143)
+Fout=1836.40 (A=030, B=143)
+Fout=1836.60 (A=031, B=143)
+Fout=1836.80 (A=032, B=143)
+Fout=1837.00 (A=033, B=143)
+Fout=1837.20 (A=034, B=143)
+Fout=1837.40 (A=035, B=143)
+Fout=1837.60 (A=036, B=143)
+Fout=1837.80 (A=037, B=143)
+Fout=1838.00 (A=038, B=143)
+Fout=1838.20 (A=039, B=143)
+Fout=1838.40 (A=040, B=143)
+Fout=1838.60 (A=041, B=143)
+Fout=1838.80 (A=042, B=143)
+Fout=1839.00 (A=043, B=143)
+Fout=1839.20 (A=044, B=143)
+Fout=1839.40 (A=045, B=143)
+Fout=1839.60 (A=046, B=143)
+Fout=1839.80 (A=047, B=143)
+Fout=1840.00 (A=048, B=143)
+Fout=1840.20 (A=049, B=143)
+Fout=1840.40 (A=050, B=143)
+Fout=1840.60 (A=051, B=143)
+Fout=1840.80 (A=052, B=143)
+Fout=1841.00 (A=053, B=143)
+Fout=1841.20 (A=054, B=143)
+Fout=1841.40 (A=055, B=143)
+Fout=1841.60 (A=056, B=143)
+Fout=1841.80 (A=057, B=143)
+Fout=1842.00 (A=058, B=143)
+Fout=1842.20 (A=059, B=143)
+Fout=1842.40 (A=060, B=143)
+Fout=1842.60 (A=061, B=143)
+Fout=1842.80 (A=062, B=143)
+Fout=1843.00 (A=063, B=143)
+Fout=1843.20 (A=000, B=144)
+Fout=1843.40 (A=001, B=144)
+Fout=1843.60 (A=002, B=144)
+Fout=1843.80 (A=003, B=144)
+Fout=1844.00 (A=004, B=144)
+Fout=1844.20 (A=005, B=144)
+Fout=1844.40 (A=006, B=144)
+Fout=1844.60 (A=007, B=144)
+Fout=1844.80 (A=008, B=144)
+Fout=1845.00 (A=009, B=144)
+Fout=1845.20 (A=010, B=144)
+Fout=1845.40 (A=011, B=144)
+Fout=1845.60 (A=012, B=144)
+Fout=1845.80 (A=013, B=144)
+Fout=1846.00 (A=014, B=144)
+Fout=1846.20 (A=015, B=144)
+Fout=1846.40 (A=016, B=144)
+Fout=1846.60 (A=017, B=144)
+Fout=1846.80 (A=018, B=144)
+Fout=1847.00 (A=019, B=144)
+Fout=1847.20 (A=020, B=144)
+Fout=1847.40 (A=021, B=144)
+Fout=1847.60 (A=022, B=144)
+Fout=1847.80 (A=023, B=144)
+Fout=1848.00 (A=024, B=144)
+Fout=1848.20 (A=025, B=144)
+Fout=1848.40 (A=026, B=144)
+Fout=1848.60 (A=027, B=144)
+Fout=1848.80 (A=028, B=144)
+Fout=1849.00 (A=029, B=144)
+Fout=1849.20 (A=030, B=144)
+Fout=1849.40 (A=031, B=144)
+Fout=1849.60 (A=032, B=144)
+Fout=1849.80 (A=033, B=144)
+Fout=1850.00 (A=034, B=144)
+Fout=1850.20 (A=035, B=144)
+Fout=1850.40 (A=036, B=144)
+Fout=1850.60 (A=037, B=144)
+Fout=1850.80 (A=038, B=144)
+Fout=1851.00 (A=039, B=144)
+Fout=1851.20 (A=040, B=144)
+Fout=1851.40 (A=041, B=144)
+Fout=1851.60 (A=042, B=144)
+Fout=1851.80 (A=043, B=144)
+Fout=1852.00 (A=044, B=144)
+Fout=1852.20 (A=045, B=144)
+Fout=1852.40 (A=046, B=144)
+Fout=1852.60 (A=047, B=144)
+Fout=1852.80 (A=048, B=144)
+Fout=1853.00 (A=049, B=144)
+Fout=1853.20 (A=050, B=144)
+Fout=1853.40 (A=051, B=144)
+Fout=1853.60 (A=052, B=144)
+Fout=1853.80 (A=053, B=144)
+Fout=1854.00 (A=054, B=144)
+Fout=1854.20 (A=055, B=144)
+Fout=1854.40 (A=056, B=144)
+Fout=1854.60 (A=057, B=144)
+Fout=1854.80 (A=058, B=144)
+Fout=1855.00 (A=059, B=144)
+Fout=1855.20 (A=060, B=144)
+Fout=1855.40 (A=061, B=144)
+Fout=1855.60 (A=062, B=144)
+Fout=1855.80 (A=063, B=144)
+Fout=1856.00 (A=000, B=145)
+Fout=1856.20 (A=001, B=145)
+Fout=1856.40 (A=002, B=145)
+Fout=1856.60 (A=003, B=145)
+Fout=1856.80 (A=004, B=145)
+Fout=1857.00 (A=005, B=145)
+Fout=1857.20 (A=006, B=145)
+Fout=1857.40 (A=007, B=145)
+Fout=1857.60 (A=008, B=145)
+Fout=1857.80 (A=009, B=145)
+Fout=1858.00 (A=010, B=145)
+Fout=1858.20 (A=011, B=145)
+Fout=1858.40 (A=012, B=145)
+Fout=1858.60 (A=013, B=145)
+Fout=1858.80 (A=014, B=145)
+Fout=1859.00 (A=015, B=145)
+Fout=1859.20 (A=016, B=145)
+Fout=1859.40 (A=017, B=145)
+Fout=1859.60 (A=018, B=145)
+Fout=1859.80 (A=019, B=145)
+Fout=1860.00 (A=020, B=145)
+Fout=1860.20 (A=021, B=145)
+Fout=1860.40 (A=022, B=145)
+Fout=1860.60 (A=023, B=145)
+Fout=1860.80 (A=024, B=145)
+Fout=1861.00 (A=025, B=145)
+Fout=1861.20 (A=026, B=145)
+Fout=1861.40 (A=027, B=145)
+Fout=1861.60 (A=028, B=145)
+Fout=1861.80 (A=029, B=145)
+Fout=1862.00 (A=030, B=145)
+Fout=1862.20 (A=031, B=145)
+Fout=1862.40 (A=032, B=145)
+Fout=1862.60 (A=033, B=145)
+Fout=1862.80 (A=034, B=145)
+Fout=1863.00 (A=035, B=145)
+Fout=1863.20 (A=036, B=145)
+Fout=1863.40 (A=037, B=145)
+Fout=1863.60 (A=038, B=145)
+Fout=1863.80 (A=039, B=145)
+Fout=1864.00 (A=040, B=145)
+Fout=1864.20 (A=041, B=145)
+Fout=1864.40 (A=042, B=145)
+Fout=1864.60 (A=043, B=145)
+Fout=1864.80 (A=044, B=145)
+Fout=1865.00 (A=045, B=145)
+Fout=1865.20 (A=046, B=145)
+Fout=1865.40 (A=047, B=145)
+Fout=1865.60 (A=048, B=145)
+Fout=1865.80 (A=049, B=145)
+Fout=1866.00 (A=050, B=145)
+Fout=1866.20 (A=051, B=145)
+Fout=1866.40 (A=052, B=145)
+Fout=1866.60 (A=053, B=145)
+Fout=1866.80 (A=054, B=145)
+Fout=1867.00 (A=055, B=145)
+Fout=1867.20 (A=056, B=145)
+Fout=1867.40 (A=057, B=145)
+Fout=1867.60 (A=058, B=145)
+Fout=1867.80 (A=059, B=145)
+Fout=1868.00 (A=060, B=145)
+Fout=1868.20 (A=061, B=145)
+Fout=1868.40 (A=062, B=145)
+Fout=1868.60 (A=063, B=145)
+Fout=1868.80 (A=000, B=146)
+Fout=1869.00 (A=001, B=146)
+Fout=1869.20 (A=002, B=146)
+Fout=1869.40 (A=003, B=146)
+Fout=1869.60 (A=004, B=146)
+Fout=1869.80 (A=005, B=146)
+Fout=1870.00 (A=006, B=146)
+Fout=1870.20 (A=007, B=146)
+Fout=1870.40 (A=008, B=146)
+Fout=1870.60 (A=009, B=146)
+Fout=1870.80 (A=010, B=146)
+Fout=1871.00 (A=011, B=146)
+Fout=1871.20 (A=012, B=146)
+Fout=1871.40 (A=013, B=146)
+Fout=1871.60 (A=014, B=146)
+Fout=1871.80 (A=015, B=146)
+Fout=1872.00 (A=016, B=146)
+Fout=1872.20 (A=017, B=146)
+Fout=1872.40 (A=018, B=146)
+Fout=1872.60 (A=019, B=146)
+Fout=1872.80 (A=020, B=146)
+Fout=1873.00 (A=021, B=146)
+Fout=1873.20 (A=022, B=146)
+Fout=1873.40 (A=023, B=146)
+Fout=1873.60 (A=024, B=146)
+Fout=1873.80 (A=025, B=146)
+Fout=1874.00 (A=026, B=146)
+Fout=1874.20 (A=027, B=146)
+Fout=1874.40 (A=028, B=146)
+Fout=1874.60 (A=029, B=146)
+Fout=1874.80 (A=030, B=146)
+Fout=1875.00 (A=031, B=146)
+Fout=1875.20 (A=032, B=146)
+Fout=1875.40 (A=033, B=146)
+Fout=1875.60 (A=034, B=146)
+Fout=1875.80 (A=035, B=146)
+Fout=1876.00 (A=036, B=146)
+Fout=1876.20 (A=037, B=146)
+Fout=1876.40 (A=038, B=146)
+Fout=1876.60 (A=039, B=146)
+Fout=1876.80 (A=040, B=146)
+Fout=1877.00 (A=041, B=146)
+Fout=1877.20 (A=042, B=146)
+Fout=1877.40 (A=043, B=146)
+Fout=1877.60 (A=044, B=146)
+Fout=1877.80 (A=045, B=146)
+Fout=1878.00 (A=046, B=146)
+Fout=1878.20 (A=047, B=146)
+Fout=1878.40 (A=048, B=146)
+Fout=1878.60 (A=049, B=146)
+Fout=1878.80 (A=050, B=146)
+Fout=1879.00 (A=051, B=146)
+Fout=1879.20 (A=052, B=146)
+Fout=1879.40 (A=053, B=146)
+Fout=1879.60 (A=054, B=146)
+Fout=1879.80 (A=055, B=146)
+Fout=1880.00 (A=056, B=146)
+Fout=1880.20 (A=057, B=146)
+Fout=1880.40 (A=058, B=146)
+Fout=1880.60 (A=059, B=146)
+Fout=1880.80 (A=060, B=146)
+Fout=1881.00 (A=061, B=146)
+Fout=1881.20 (A=062, B=146)
+Fout=1881.40 (A=063, B=146)
+Fout=1881.60 (A=000, B=147)
+Fout=1881.80 (A=001, B=147)
+Fout=1882.00 (A=002, B=147)
+Fout=1882.20 (A=003, B=147)
+Fout=1882.40 (A=004, B=147)
+Fout=1882.60 (A=005, B=147)
+Fout=1882.80 (A=006, B=147)
+Fout=1883.00 (A=007, B=147)
+Fout=1883.20 (A=008, B=147)
+Fout=1883.40 (A=009, B=147)
+Fout=1883.60 (A=010, B=147)
+Fout=1883.80 (A=011, B=147)
+Fout=1884.00 (A=012, B=147)
+Fout=1884.20 (A=013, B=147)
+Fout=1884.40 (A=014, B=147)
+Fout=1884.60 (A=015, B=147)
+Fout=1884.80 (A=016, B=147)
+Fout=1885.00 (A=017, B=147)
+Fout=1885.20 (A=018, B=147)
+Fout=1885.40 (A=019, B=147)
+Fout=1885.60 (A=020, B=147)
+Fout=1885.80 (A=021, B=147)
+Fout=1886.00 (A=022, B=147)
+Fout=1886.20 (A=023, B=147)
+Fout=1886.40 (A=024, B=147)
+Fout=1886.60 (A=025, B=147)
+Fout=1886.80 (A=026, B=147)
+Fout=1887.00 (A=027, B=147)
+Fout=1887.20 (A=028, B=147)
+Fout=1887.40 (A=029, B=147)
+Fout=1887.60 (A=030, B=147)
+Fout=1887.80 (A=031, B=147)
+Fout=1888.00 (A=032, B=147)
+Fout=1888.20 (A=033, B=147)
+Fout=1888.40 (A=034, B=147)
+Fout=1888.60 (A=035, B=147)
+Fout=1888.80 (A=036, B=147)
+Fout=1889.00 (A=037, B=147)
+Fout=1889.20 (A=038, B=147)
+Fout=1889.40 (A=039, B=147)
+Fout=1889.60 (A=040, B=147)
+Fout=1889.80 (A=041, B=147)
+Fout=1890.00 (A=042, B=147)
+Fout=1890.20 (A=043, B=147)
+Fout=1890.40 (A=044, B=147)
+Fout=1890.60 (A=045, B=147)
+Fout=1890.80 (A=046, B=147)
+Fout=1891.00 (A=047, B=147)
+Fout=1891.20 (A=048, B=147)
+Fout=1891.40 (A=049, B=147)
+Fout=1891.60 (A=050, B=147)
+Fout=1891.80 (A=051, B=147)
+Fout=1892.00 (A=052, B=147)
+Fout=1892.20 (A=053, B=147)
+Fout=1892.40 (A=054, B=147)
+Fout=1892.60 (A=055, B=147)
+Fout=1892.80 (A=056, B=147)
+Fout=1893.00 (A=057, B=147)
+Fout=1893.20 (A=058, B=147)
+Fout=1893.40 (A=059, B=147)
+Fout=1893.60 (A=060, B=147)
+Fout=1893.80 (A=061, B=147)
+Fout=1894.00 (A=062, B=147)
+Fout=1894.20 (A=063, B=147)
+Fout=1894.40 (A=000, B=148)
+Fout=1894.60 (A=001, B=148)
+Fout=1894.80 (A=002, B=148)
+Fout=1895.00 (A=003, B=148)
+Fout=1895.20 (A=004, B=148)
+Fout=1895.40 (A=005, B=148)
+Fout=1895.60 (A=006, B=148)
+Fout=1895.80 (A=007, B=148)
+Fout=1896.00 (A=008, B=148)
+Fout=1896.20 (A=009, B=148)
+Fout=1896.40 (A=010, B=148)
+Fout=1896.60 (A=011, B=148)
+Fout=1896.80 (A=012, B=148)
+Fout=1897.00 (A=013, B=148)
+Fout=1897.20 (A=014, B=148)
+Fout=1897.40 (A=015, B=148)
+Fout=1897.60 (A=016, B=148)
+Fout=1897.80 (A=017, B=148)
+Fout=1898.00 (A=018, B=148)
+Fout=1898.20 (A=019, B=148)
+Fout=1898.40 (A=020, B=148)
+Fout=1898.60 (A=021, B=148)
+Fout=1898.80 (A=022, B=148)
+Fout=1899.00 (A=023, B=148)
+Fout=1899.20 (A=024, B=148)
+Fout=1899.40 (A=025, B=148)
+Fout=1899.60 (A=026, B=148)
+Fout=1899.80 (A=027, B=148)
+Fout=1900.00 (A=028, B=148)
+Fout=1900.20 (A=029, B=148)
+Fout=1900.40 (A=030, B=148)
+Fout=1900.60 (A=031, B=148)
+Fout=1900.80 (A=032, B=148)
+Fout=1901.00 (A=033, B=148)
+Fout=1901.20 (A=034, B=148)
+Fout=1901.40 (A=035, B=148)
+Fout=1901.60 (A=036, B=148)
+Fout=1901.80 (A=037, B=148)
+Fout=1902.00 (A=038, B=148)
+Fout=1902.20 (A=039, B=148)
+Fout=1902.40 (A=040, B=148)
+Fout=1902.60 (A=041, B=148)
+Fout=1902.80 (A=042, B=148)
+Fout=1903.00 (A=043, B=148)
+Fout=1903.20 (A=044, B=148)
+Fout=1903.40 (A=045, B=148)
+Fout=1903.60 (A=046, B=148)
+Fout=1903.80 (A=047, B=148)
+Fout=1904.00 (A=048, B=148)
+Fout=1904.20 (A=049, B=148)
+Fout=1904.40 (A=050, B=148)
+Fout=1904.60 (A=051, B=148)
+Fout=1904.80 (A=052, B=148)
+Fout=1905.00 (A=053, B=148)
+Fout=1905.20 (A=054, B=148)
+Fout=1905.40 (A=055, B=148)
+Fout=1905.60 (A=056, B=148)
+Fout=1905.80 (A=057, B=148)
+Fout=1906.00 (A=058, B=148)
+Fout=1906.20 (A=059, B=148)
+Fout=1906.40 (A=060, B=148)
+Fout=1906.60 (A=061, B=148)
+Fout=1906.80 (A=062, B=148)
+Fout=1907.00 (A=063, B=148)
+Fout=1907.20 (A=000, B=149)
+Fout=1907.40 (A=001, B=149)
+Fout=1907.60 (A=002, B=149)
+Fout=1907.80 (A=003, B=149)
+Fout=1908.00 (A=004, B=149)
+Fout=1908.20 (A=005, B=149)
+Fout=1908.40 (A=006, B=149)
+Fout=1908.60 (A=007, B=149)
+Fout=1908.80 (A=008, B=149)
+Fout=1909.00 (A=009, B=149)
+Fout=1909.20 (A=010, B=149)
+Fout=1909.40 (A=011, B=149)
+Fout=1909.60 (A=012, B=149)
+Fout=1909.80 (A=013, B=149)
+Fout=1910.00 (A=014, B=149)
+Fout=1910.20 (A=015, B=149)
+Fout=1910.40 (A=016, B=149)
+Fout=1910.60 (A=017, B=149)
+Fout=1910.80 (A=018, B=149)
+Fout=1911.00 (A=019, B=149)
+Fout=1911.20 (A=020, B=149)
+Fout=1911.40 (A=021, B=149)
+Fout=1911.60 (A=022, B=149)
+Fout=1911.80 (A=023, B=149)
+Fout=1912.00 (A=024, B=149)
+Fout=1912.20 (A=025, B=149)
+Fout=1912.40 (A=026, B=149)
+Fout=1912.60 (A=027, B=149)
+Fout=1912.80 (A=028, B=149)
+Fout=1913.00 (A=029, B=149)
+Fout=1913.20 (A=030, B=149)
+Fout=1913.40 (A=031, B=149)
+Fout=1913.60 (A=032, B=149)
+Fout=1913.80 (A=033, B=149)
+Fout=1914.00 (A=034, B=149)
+Fout=1914.20 (A=035, B=149)
+Fout=1914.40 (A=036, B=149)
+Fout=1914.60 (A=037, B=149)
+Fout=1914.80 (A=038, B=149)
+Fout=1915.00 (A=039, B=149)
+Fout=1915.20 (A=040, B=149)
+Fout=1915.40 (A=041, B=149)
+Fout=1915.60 (A=042, B=149)
+Fout=1915.80 (A=043, B=149)
+Fout=1916.00 (A=044, B=149)
+Fout=1916.20 (A=045, B=149)
+Fout=1916.40 (A=046, B=149)
+Fout=1916.60 (A=047, B=149)
+Fout=1916.80 (A=048, B=149)
+Fout=1917.00 (A=049, B=149)
+Fout=1917.20 (A=050, B=149)
+Fout=1917.40 (A=051, B=149)
+Fout=1917.60 (A=052, B=149)
+Fout=1917.80 (A=053, B=149)
+Fout=1918.00 (A=054, B=149)
+Fout=1918.20 (A=055, B=149)
+Fout=1918.40 (A=056, B=149)
+Fout=1918.60 (A=057, B=149)
+Fout=1918.80 (A=058, B=149)
+Fout=1919.00 (A=059, B=149)
+Fout=1919.20 (A=060, B=149)
+Fout=1919.40 (A=061, B=149)
+Fout=1919.60 (A=062, B=149)
+Fout=1919.80 (A=063, B=149)
diff --git a/src/host/rita_pll/rita_pll_notes.txt b/src/host/rita_pll/rita_pll_notes.txt
new file mode 100644
index 00000000..8557d3ae
--- /dev/null
+++ b/src/host/rita_pll/rita_pll_notes.txt
@@ -0,0 +1,8 @@
+
+Regular Operation as per DS GSM SPEC
+
+GSM900 Tx: 870.4 ... 921.4 MHz 880.0 ... 914.8
+GSM900 Rx: 864.4 ... 966.2 MHz 925.0 ... 959.8
+
+GSM1800 Tx: 1702.4 ... 1919.8 MHz 1710.2 ... 1784.8
+GSM1800 Rx: 1804.8 ... 1996.4 MHz 1805.2 ... 1879.8
diff --git a/src/shared/libosmocore/.gitignore b/src/shared/libosmocore/.gitignore
new file mode 100644
index 00000000..06904fa0
--- /dev/null
+++ b/src/shared/libosmocore/.gitignore
@@ -0,0 +1,30 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.o
+*.lo
+*.la
+*.pc
+aclocal.m4
+m4/*.m4
+autom4te.cache
+config.h*
+config.sub
+config.log
+config.status
+config.guess
+configure
+depcomp
+missing
+ltmain.sh
+install-sh
+stamp-h1
+libtool
+
+.tarball-version
+.version
+
+tests/sms/sms_test
+tests/timer/timer_test
+
diff --git a/src/shared/libosmocore/COPYING b/src/shared/libosmocore/COPYING
new file mode 100644
index 00000000..d511905c
--- /dev/null
+++ b/src/shared/libosmocore/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src/shared/libosmocore/Makefile.am b/src/shared/libosmocore/Makefile.am
new file mode 100644
index 00000000..81da6294
--- /dev/null
+++ b/src/shared/libosmocore/Makefile.am
@@ -0,0 +1,14 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+ACLOCAL_AMFLAGS = -I m4
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+SUBDIRS = include src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libosmocore.pc libosmovty.pc
+
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/src/shared/libosmocore/configure.in b/src/shared/libosmocore/configure.in
new file mode 100644
index 00000000..e3e178c0
--- /dev/null
+++ b/src/shared/libosmocore/configure.in
@@ -0,0 +1,72 @@
+AC_INIT([libosmocore],
+ m4_esyscmd([./git-version-gen .tarball-version]),
+ [openbsc-devel@lists.openbsc.org])
+
+AM_INIT_AUTOMAKE([dist-bzip2])
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl checks for programs
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+LT_INIT
+AC_PROG_LIBTOOL
+
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl checks for header files
+AC_HEADER_STDC
+AC_CHECK_HEADERS(execinfo.h sys/select.h)
+
+# The following test is taken from WebKit's webkit.m4
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -fvisibility=hidden "
+AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
+AC_COMPILE_IFELSE([char foo;],
+ [ AC_MSG_RESULT([yes])
+ SYMBOL_VISIBILITY="-fvisibility=hidden"],
+ AC_MSG_RESULT([no]))
+CFLAGS="$saved_CFLAGS"
+AC_SUBST(SYMBOL_VISIBILITY)
+
+dnl Generate the output
+AM_CONFIG_HEADER(config.h)
+
+AC_ARG_ENABLE(talloc,
+ [ --disable-talloc Disable building talloc memory allocator ],
+ [enable_talloc=0], [enable_talloc=1])
+AM_CONDITIONAL(ENABLE_TALLOC, test "x$enable_talloc" = "x1")
+
+AC_ARG_ENABLE(plugin,
+ [ --disable-plugin Disable support for dlopen plugins ],
+ [enable_plugin=0], [enable_plugin=1])
+AM_CONDITIONAL(ENABLE_PLUGIN, test "x$enable_plugin" = "x1")
+
+AC_ARG_ENABLE(tests,
+ [ --disable-tests Disable building test programs ],
+ [enable_tests=0], [enable_tests=1])
+AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1")
+
+AC_ARG_ENABLE(vty,
+ [ --disable-vty Disable building VTY telnet interface ],
+ [enable_vty=0], [enable_vty=1])
+AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1")
+
+
+AC_OUTPUT(
+ libosmocore.pc
+ libosmovty.pc
+ include/osmocom/Makefile
+ include/osmocom/vty/Makefile
+ include/osmocom/crypt/Makefile
+ include/osmocore/Makefile
+ include/osmocore/protocol/Makefile
+ include/Makefile
+ src/Makefile
+ src/vty/Makefile
+ tests/Makefile
+ tests/timer/Makefile
+ tests/sms/Makefile
+ Makefile)
diff --git a/src/shared/libosmocore/git-version-gen b/src/shared/libosmocore/git-version-gen
new file mode 100755
index 00000000..42cf3d2b
--- /dev/null
+++ b/src/shared/libosmocore/git-version-gen
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-01-28.01
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+ 1) ;;
+ *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || exit 1
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test -z "$v" \
+ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+ : # use $v
+elif
+ v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && case $v in
+ [0-9]*) ;;
+ v[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+ case $v in
+ *-*-*) : git describe is okay three part flavor ;;
+ *-*)
+ : git describe is older two part flavor
+ # Recreate the number of commits and rewrite such that the
+ # result is the same as if we were using the newer version
+ # of git describe.
+ vtag=`echo "$v" | sed 's/-.*//'`
+ numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+ v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+ ;;
+ esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+ v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+ v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/src/shared/libosmocore/include/Makefile.am b/src/shared/libosmocore/include/Makefile.am
new file mode 100644
index 00000000..185c6968
--- /dev/null
+++ b/src/shared/libosmocore/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = osmocom osmocore
diff --git a/src/shared/libosmocore/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am
new file mode 100644
index 00000000..fd9074cd
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/Makefile.am
@@ -0,0 +1,5 @@
+if ENABLE_VTY
+SUBDIRS = vty crypt
+else
+SUBDIRS = crypt
+endif
diff --git a/src/shared/libosmocore/include/osmocom/crypt/Makefile.am b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am
new file mode 100644
index 00000000..7ce69fdd
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am
@@ -0,0 +1,3 @@
+osmocrypt_HEADERS = gprs_cipher.h
+
+osmocryptdir = $(includedir)/osmocom/crypt
diff --git a/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
new file mode 100644
index 00000000..3e514ec7
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h
@@ -0,0 +1,54 @@
+#ifndef _GPRS_CIPHER_H
+#define _GPRS_CIPHER_H
+
+#include <osmocore/linuxlist.h>
+
+#define GSM0464_CIPH_MAX_BLOCK 1523
+
+enum gprs_ciph_algo {
+ GPRS_ALGO_GEA0,
+ GPRS_ALGO_GEA1,
+ GPRS_ALGO_GEA2,
+ GPRS_ALGO_GEA3,
+ _GPRS_ALGO_NUM
+};
+
+enum gprs_cipher_direction {
+ GPRS_CIPH_MS2SGSN,
+ GPRS_CIPH_SGSN2MS,
+};
+
+/* An implementation of a GPRS cipher */
+struct gprs_cipher_impl {
+ struct llist_head list;
+ enum gprs_ciph_algo algo;
+ const char *name;
+ unsigned int priority;
+
+ /* As specified in 04.64 Annex A. Uses Kc, IV and direction
+ * to generate the 1523 bytes cipher stream that need to be
+ * XORed wit the plaintext for encrypt / ciphertext for decrypt */
+ int (*run)(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
+ enum gprs_cipher_direction direction);
+};
+
+/* register a cipher with the core (from a plugin) */
+int gprs_cipher_register(struct gprs_cipher_impl *ciph);
+
+/* load all available GPRS cipher plugins */
+int gprs_cipher_load(const char *path);
+
+/* function to be called by core code */
+int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo,
+ uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir);
+
+/* Do we have an implementation for this cipher? */
+int gprs_cipher_supported(enum gprs_ciph_algo algo);
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc);
+
+/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */
+uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc);
+
+#endif /* _GPRS_CIPHER_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am
new file mode 100644
index 00000000..d2f0616d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/Makefile.am
@@ -0,0 +1,4 @@
+osmovty_HEADERS = buffer.h command.h vector.h vty.h \
+ telnet_interface.h logging.h
+
+osmovtydir = $(includedir)/osmocom/vty
diff --git a/src/shared/libosmocore/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h
new file mode 100644
index 00000000..c9467a91
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/buffer.h
@@ -0,0 +1,102 @@
+/*
+ * Buffering to output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_BUFFER_H
+#define _ZEBRA_BUFFER_H
+
+#include <sys/types.h>
+
+/* Create a new buffer. Memory will be allocated in chunks of the given
+ size. If the argument is 0, the library will supply a reasonable
+ default size suitable for buffering socket I/O. */
+struct buffer *buffer_new(void *ctx, size_t);
+
+/* Free all data in the buffer. */
+void buffer_reset(struct buffer *);
+
+/* This function first calls buffer_reset to release all buffered data.
+ Then it frees the struct buffer itself. */
+void buffer_free(struct buffer *);
+
+/* Add the given data to the end of the buffer. */
+extern void buffer_put(struct buffer *, const void *, size_t);
+/* Add a single character to the end of the buffer. */
+extern void buffer_putc(struct buffer *, u_char);
+/* Add a NUL-terminated string to the end of the buffer. */
+extern void buffer_putstr(struct buffer *, const char *);
+
+/* Combine all accumulated (and unflushed) data inside the buffer into a
+ single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
+ that this function does not alter the state of the buffer, so the data
+ is still inside waiting to be flushed. */
+char *buffer_getstr(struct buffer *);
+
+/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
+int buffer_empty(struct buffer *);
+
+typedef enum {
+ /* An I/O error occurred. The buffer should be destroyed and the
+ file descriptor should be closed. */
+ BUFFER_ERROR = -1,
+
+ /* The data was written successfully, and the buffer is now empty
+ (there is no pending data waiting to be flushed). */
+ BUFFER_EMPTY = 0,
+
+ /* There is pending data in the buffer waiting to be flushed. Please
+ try flushing the buffer when select indicates that the file descriptor
+ is writeable. */
+ BUFFER_PENDING = 1
+} buffer_status_t;
+
+/* Try to write this data to the file descriptor. Any data that cannot
+ be written immediately is added to the buffer queue. */
+extern buffer_status_t buffer_write(struct buffer *, int fd,
+ const void *, size_t);
+
+/* This function attempts to flush some (but perhaps not all) of
+ the queued data to the given file descriptor. */
+extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
+
+/* The following 2 functions (buffer_flush_all and buffer_flush_window)
+ are for use in lib/vty.c only. They should not be used elsewhere. */
+
+/* Call buffer_flush_available repeatedly until either all data has been
+ flushed, or an I/O error has been encountered, or the operation would
+ block. */
+extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
+
+/* Attempt to write enough data to the given fd to fill a window of the
+ given width and height (and remove the data written from the buffer).
+
+ If !no_more, then a message saying " --More-- " is appended.
+ If erase is true, then first overwrite the previous " --More-- " message
+ with spaces.
+
+ Any write error (including EAGAIN or EINTR) will cause this function
+ to return -1 (because the logic for handling the erase and more features
+ is too complicated to retry the write later).
+*/
+extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
+ int height, int erase, int no_more);
+
+#endif /* _ZEBRA_BUFFER_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h
new file mode 100644
index 00000000..69e9e772
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/command.h
@@ -0,0 +1,349 @@
+/*
+ * Zebra configuration command interface routine
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that 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 GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_COMMAND_H
+#define _ZEBRA_COMMAND_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "vector.h"
+#include "vty.h"
+
+/* Host configuration variable */
+struct host {
+ /* Host name of this router. */
+ char *name;
+
+ /* Password for vty interface. */
+ char *password;
+ char *password_encrypt;
+
+ /* Enable password */
+ char *enable;
+ char *enable_encrypt;
+
+ /* System wide terminal lines. */
+ int lines;
+
+ /* Log filename. */
+ char *logfile;
+
+ /* config file name of this host */
+ char *config;
+
+ /* Flags for services */
+ int advanced;
+ int encrypt;
+
+ /* Banner configuration. */
+ const char *motd;
+ char *motdfile;
+
+ const struct vty_app_info *app_info;
+};
+
+/* There are some command levels which called from command node. */
+enum node_type {
+ AUTH_NODE, /* Authentication mode of vty interface. */
+ VIEW_NODE, /* View node. Default mode of vty interface. */
+ AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
+ ENABLE_NODE, /* Enable node. */
+ CONFIG_NODE, /* Config node. Default mode of config file. */
+ SERVICE_NODE, /* Service node. */
+ DEBUG_NODE, /* Debug node. */
+
+ VTY_NODE, /* Vty node. */
+
+ _LAST_OSMOVTY_NODE
+};
+
+/* Node which has some commands and prompt string and configuration
+ function pointer . */
+struct cmd_node {
+ /* Node index. */
+ enum node_type node;
+
+ /* Prompt character at vty interface. */
+ const char *prompt;
+
+ /* Is this node's configuration goes to vtysh ? */
+ int vtysh;
+
+ /* Node's configuration write function */
+ int (*func) (struct vty *);
+
+ /* Vector of this node's command list. */
+ vector cmd_vector;
+};
+
+enum {
+ CMD_ATTR_DEPRECATED = 1,
+ CMD_ATTR_HIDDEN,
+};
+
+/* Structure of command element. */
+struct cmd_element {
+ const char *string; /* Command specification by string. */
+ int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
+ const char *doc; /* Documentation of this command. */
+ int daemon; /* Daemon to which this command belong. */
+ vector strvec; /* Pointing out each description vector. */
+ unsigned int cmdsize; /* Command index count. */
+ char *config; /* Configuration string */
+ vector subconfig; /* Sub configuration string */
+ u_char attr; /* Command attributes */
+};
+
+/* Command description structure. */
+struct desc {
+ const char *cmd; /* Command string. */
+ const char *str; /* Command's description. */
+};
+
+/* Return value of the commands. */
+#define CMD_SUCCESS 0
+#define CMD_WARNING 1
+#define CMD_ERR_NO_MATCH 2
+#define CMD_ERR_AMBIGUOUS 3
+#define CMD_ERR_INCOMPLETE 4
+#define CMD_ERR_EXEED_ARGC_MAX 5
+#define CMD_ERR_NOTHING_TODO 6
+#define CMD_COMPLETE_FULL_MATCH 7
+#define CMD_COMPLETE_MATCH 8
+#define CMD_COMPLETE_LIST_MATCH 9
+#define CMD_SUCCESS_DAEMON 10
+
+/* Argc max counts. */
+#define CMD_ARGC_MAX 25
+
+/* Turn off these macros when uisng cpp with extract.pl */
+#ifndef VTYSH_EXTRACT_PL
+
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ static struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ };
+
+/* global (non static) cmd_element */
+#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ };
+
+#define DEFUN_CMD_FUNC_DECL(funcname) \
+ static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \
+
+#define DEFUN_CMD_FUNC_TEXT(funcname) \
+ static int funcname \
+ (struct cmd_element *self, struct vty *vty, int argc, const char *argv[])
+
+/* DEFUN for vty command interafce. Little bit hacky ;-). */
+#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* global (non static) cmd_element */
+#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+
+/* DEFUN_NOSH for commands that vtysh should ignore */
+#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN(funcname, cmdname, cmdstr, helpstr)
+
+/* DEFSH for vtysh. */
+#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+
+/* DEFUN + DEFSH */
+#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* DEFUN + DEFSH with attributes */
+#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+/* ALIAS macro which define existing command's alias. */
+#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+/* global (non static) cmd_element */
+#define gALIAS(funcname, cmdname, cmdstr, helpstr) \
+ gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+
+#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+
+#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+
+#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+
+#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+
+#endif /* VTYSH_EXTRACT_PL */
+
+/* Some macroes */
+#define CMD_OPTION(S) ((S[0]) == '[')
+#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
+#define CMD_VARARG(S) ((S[0]) == '.')
+#define CMD_RANGE(S) ((S[0] == '<'))
+
+#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+
+/* Common descriptions. */
+#define SHOW_STR "Show running system information\n"
+#define IP_STR "IP information\n"
+#define IPV6_STR "IPv6 information\n"
+#define NO_STR "Negate a command or set its defaults\n"
+#define CLEAR_STR "Reset functions\n"
+#define RIP_STR "RIP information\n"
+#define BGP_STR "BGP information\n"
+#define OSPF_STR "OSPF information\n"
+#define NEIGHBOR_STR "Specify neighbor router\n"
+#define DEBUG_STR "Debugging functions (see also 'undebug')\n"
+#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
+#define ROUTER_STR "Enable a routing process\n"
+#define AS_STR "AS number\n"
+#define MBGP_STR "MBGP information\n"
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+#define OUT_STR "Filter outgoing routing updates\n"
+#define IN_STR "Filter incoming routing updates\n"
+#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n"
+#define OSPF6_NUMBER_STR "Specify by number\n"
+#define INTERFACE_STR "Interface infomation\n"
+#define IFNAME_STR "Interface name(e.g. ep0)\n"
+#define IP6_STR "IPv6 Information\n"
+#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"
+#define OSPF6_ROUTER_STR "Enable a routing process\n"
+#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n"
+#define SECONDS_STR "<1-65535> Seconds\n"
+#define ROUTE_STR "Routing Table\n"
+#define PREFIX_LIST_STR "Build a prefix list\n"
+#define OSPF6_DUMP_TYPE_LIST \
+"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
+#define ISIS_STR "IS-IS information\n"
+#define AREA_TAG_STR "[area tag]\n"
+
+#define CONF_BACKUP_EXT ".sav"
+
+/* IPv4 only machine should not accept IPv6 address for peer's IP
+ address. So we replace VTY command string like below. */
+#ifdef HAVE_IPV6
+#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) "
+#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) "
+#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n"
+#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n"
+#else
+#define NEIGHBOR_CMD "neighbor A.B.C.D "
+#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D "
+#define NEIGHBOR_ADDR_STR "Neighbor address\n"
+#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) "
+#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"
+#endif /* HAVE_IPV6 */
+
+/* Prototypes. */
+void install_node(struct cmd_node *, int (*)(struct vty *));
+void install_default(enum node_type);
+void install_element(enum node_type, struct cmd_element *);
+void install_element_ve(struct cmd_element *cmd);
+void sort_node();
+
+/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
+ string with a space between each element (allocated using
+ XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
+char *argv_concat(const char **argv, int argc, int shift);
+
+vector cmd_make_strvec(const char *);
+void cmd_free_strvec(vector);
+vector cmd_describe_command();
+char **cmd_complete_command();
+const char *cmd_prompt(enum node_type);
+int config_from_file(struct vty *, FILE *);
+enum node_type node_parent(enum node_type);
+int cmd_execute_command(vector, struct vty *, struct cmd_element **, int);
+int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **);
+void config_replace_string(struct cmd_element *, char *, ...);
+void cmd_init(int);
+
+/* Export typical functions. */
+extern struct cmd_element config_exit_cmd;
+extern struct cmd_element config_help_cmd;
+extern struct cmd_element config_list_cmd;
+char *host_config_file();
+void host_config_set(const char *);
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void print_version(int print_copyright);
+
+extern void *tall_vty_cmd_ctx;
+
+#endif /* _ZEBRA_COMMAND_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h
new file mode 100644
index 00000000..f8ffbc3e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/logging.h
@@ -0,0 +1,7 @@
+#ifndef _VTY_LOGGING_H
+#define _VTY_LOGGING_H
+
+#define LOGGING_STR "Configure log message to this terminal\n"
+#define FILTER_STR "Filter log messages\n"
+
+#endif /* _VTY_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
new file mode 100644
index 00000000..444e6497
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
@@ -0,0 +1,40 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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.
+ *
+ */
+
+#ifndef TELNET_INTERFACE_H
+#define TELNET_INTERFACE_H
+
+#include <osmocore/logging.h>
+#include <osmocore/select.h>
+
+#include <osmocom/vty/vty.h>
+
+struct telnet_connection {
+ struct llist_head entry;
+ void *priv;
+ struct bsc_fd fd;
+ struct vty *vty;
+ struct log_target *dbg;
+};
+
+
+int telnet_init(void *tall_ctx, void *priv, int port);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h
new file mode 100644
index 00000000..22a184d6
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/vector.h
@@ -0,0 +1,64 @@
+/*
+ * Generic vector interface header.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that 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 GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VECTOR_H
+#define _ZEBRA_VECTOR_H
+
+/* struct for vector */
+struct _vector {
+ unsigned int active; /* number of active slots */
+ unsigned int alloced; /* number of allocated slot */
+ void **index; /* index to data */
+};
+typedef struct _vector *vector;
+
+#define VECTOR_MIN_SIZE 1
+
+/* (Sometimes) usefull macros. This macro convert index expression to
+ array expression. */
+/* Reference slot at given index, caller must ensure slot is active */
+#define vector_slot(V,I) ((V)->index[(I)])
+/* Number of active slots.
+ * Note that this differs from vector_count() as it the count returned
+ * will include any empty slots
+ */
+#define vector_active(V) ((V)->active)
+
+/* Prototypes. */
+vector vector_init(unsigned int size);
+void vector_ensure(vector v, unsigned int num);
+int vector_empty_slot(vector v);
+int vector_set(vector v, void *val);
+int vector_set_index(vector v, unsigned int i, void *val);
+void vector_unset(vector v, unsigned int i);
+unsigned int vector_count(vector v);
+void vector_only_wrapper_free(vector v);
+void vector_only_index_free(void *index);
+void vector_free(vector v);
+vector vector_copy(vector v);
+
+void *vector_lookup(vector, unsigned int);
+void *vector_lookup_ensure(vector, unsigned int);
+
+extern void *tall_vty_vec_ctx;
+
+#endif /* _ZEBRA_VECTOR_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h
new file mode 100644
index 00000000..e7399ba1
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/vty.h
@@ -0,0 +1,159 @@
+#ifndef _VTY_H
+#define _VTY_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* GCC have printf type attribute check. */
+#ifdef __GNUC__
+#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define VTY_PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/* Does the I/O error indicate that the operation should be retried later? */
+#define ERRNO_IO_RETRY(EN) \
+ (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+/* Vty read buffer size. */
+#define VTY_READ_BUFSIZ 512
+
+#define VTY_BUFSIZ 512
+#define VTY_MAXHIST 20
+
+/* Vty events */
+enum event {
+ VTY_SERV,
+ VTY_READ,
+ VTY_WRITE,
+ VTY_CLOSED,
+ VTY_TIMEOUT_RESET,
+#ifdef VTYSH
+ VTYSH_SERV,
+ VTYSH_READ,
+ VTYSH_WRITE
+#endif /* VTYSH */
+};
+
+struct vty {
+ FILE *file;
+
+ /* private data, specified by creator */
+ void *priv;
+
+ /* File descripter of this vty. */
+ int fd;
+
+ /* Is this vty connect to file or not */
+ enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type;
+
+ /* Node status of this vty */
+ int node;
+
+ /* Failure count */
+ int fail;
+
+ /* Output buffer. */
+ struct buffer *obuf;
+
+ /* Command input buffer */
+ char *buf;
+
+ /* Command cursor point */
+ int cp;
+
+ /* Command length */
+ int length;
+
+ /* Command max length. */
+ int max;
+
+ /* Histry of command */
+ char *hist[VTY_MAXHIST];
+
+ /* History lookup current point */
+ int hp;
+
+ /* History insert end point */
+ int hindex;
+
+ /* For current referencing point of interface, route-map,
+ access-list etc... */
+ void *index;
+
+ /* For multiple level index treatment such as key chain and key. */
+ void *index_sub;
+
+ /* For escape character. */
+ unsigned char escape;
+
+ /* Current vty status. */
+ enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
+
+ /* IAC handling: was the last character received the IAC
+ * (interpret-as-command) escape character (and therefore the next
+ * character will be the command code)? Refer to Telnet RFC 854. */
+ unsigned char iac;
+
+ /* IAC SB (option subnegotiation) handling */
+ unsigned char iac_sb_in_progress;
+ /* At the moment, we care only about the NAWS (window size) negotiation,
+ * and that requires just a 5-character buffer (RFC 1073):
+ * <NAWS char> <16-bit width> <16-bit height> */
+#define TELNET_NAWS_SB_LEN 5
+ unsigned char sb_buf[TELNET_NAWS_SB_LEN];
+ /* How many subnegotiation characters have we received? We just drop
+ * those that do not fit in the buffer. */
+ size_t sb_len;
+
+ /* Window width/height. */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+
+ int monitor;
+
+ /* In configure mode. */
+ int config;
+};
+
+/* Small macro to determine newline is newline only or linefeed needed. */
+#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+
+static inline char *vty_newline(struct vty *vty)
+{
+ return VTY_NEWLINE;
+}
+
+struct vty_app_info {
+ const char *name;
+ const char *version;
+ const char *copyright;
+ void *tall_ctx;
+ enum node_type (*go_parent_cb)(struct vty *vty);
+};
+
+/* Prototypes. */
+void vty_init(struct vty_app_info *app_info);
+int vty_read_config_file(const char *file_name, void *priv);
+void vty_init_vtysh (void);
+void vty_reset (void);
+struct vty *vty_new (void);
+struct vty *vty_create (int vty_sock, void *priv);
+int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3);
+int vty_out_newline(struct vty *);
+int vty_read(struct vty *vty);
+//void vty_time_print (struct vty *, int);
+void vty_close (struct vty *);
+char *vty_get_cwd (void);
+void vty_log (const char *level, const char *proto, const char *fmt, va_list);
+int vty_config_lock (struct vty *);
+int vty_config_unlock (struct vty *);
+int vty_shell (struct vty *);
+int vty_shell_serv (struct vty *);
+void vty_hello (struct vty *);
+
+void *tall_vty_ctx;
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/Makefile.am b/src/shared/libosmocore/include/osmocore/Makefile.am
new file mode 100644
index 00000000..84859a4b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/Makefile.am
@@ -0,0 +1,13 @@
+osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
+ tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
+ gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
+ gsm48_ie.h logging.h gsm0808.h rate_ctr.h gsmtap_util.h \
+ plugin.h
+
+if ENABLE_TALLOC
+osmocore_HEADERS += talloc.h
+endif
+
+osmocoredir = $(includedir)/osmocore
+
+SUBDIRS = protocol
diff --git a/src/shared/libosmocore/include/osmocore/bitvec.h b/src/shared/libosmocore/include/osmocore/bitvec.h
new file mode 100644
index 00000000..42977fb2
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/bitvec.h
@@ -0,0 +1,75 @@
+#ifndef _BITVEC_H
+#define _BITVEC_H
+
+/* bit vector utility routines */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ *
+ */
+
+
+/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are
+ * defined relative to the 0x2b padding pattern */
+enum bit_value {
+ ZERO = 0,
+ ONE = 1,
+ L = 2,
+ H = 3,
+};
+
+struct bitvec {
+ unsigned int cur_bit; /* curser to the next unused bit */
+ unsigned int data_len; /* length of data array in bytes */
+ uint8_t *data; /* pointer to data array */
+};
+
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr);
+
+/* check if the bit is L or H for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv,
+ unsigned int bitnr);
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n);
+
+/* Set a bit at given position */
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
+ enum bit_value bit);
+
+/* Set the next bit in the vector */
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
+
+/* get the next bit (low/high) inside a bitvec */
+int bitvec_get_bit_high(struct bitvec *bv);
+
+/* Set multiple bits at the current position */
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
+
+/* Add an unsigned integer (of length count bits) to current position */
+int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
+
+/* get multiple bits (based on numeric value) from current pos */
+int bitvec_get_uint(struct bitvec *bv, int num_bits);
+
+
+/* Pad the bit vector up to a certain bit position */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
+
+#endif /* _BITVEC_H */
diff --git a/src/shared/libosmocore/include/osmocore/comp128.h b/src/shared/libosmocore/include/osmocore/comp128.h
new file mode 100644
index 00000000..c37808f0
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/comp128.h
@@ -0,0 +1,22 @@
+/*
+ * COMP128 header
+ *
+ * See comp128.c for details
+ */
+
+#ifndef __COMP128_H__
+#define __COMP128_H__
+
+#include <stdint.h>
+
+/*
+ * Performs the COMP128 algorithm (used as A3/A8)
+ * ki : uint8_t [16]
+ * srand : uint8_t [16]
+ * sres : uint8_t [4]
+ * kc : uint8_t [8]
+ */
+void comp128(uint8_t *ki, uint8_t *srand, uint8_t *sres, uint8_t *kc);
+
+#endif /* __COMP128_H__ */
+
diff --git a/src/shared/libosmocore/include/osmocore/gsm0808.h b/src/shared/libosmocore/include/osmocore/gsm0808.h
new file mode 100644
index 00000000..9166e54f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm0808.h
@@ -0,0 +1,43 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ *
+ */
+#ifndef OSMOCORE_GSM0808_H
+#define OSMOCORE_GSM0808_H
+
+#include "tlv.h"
+
+struct msgb;
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, int ci);
+struct msgb *gsm0808_create_reset(void);
+struct msgb *gsm0808_create_clear_complete(void);
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause);
+struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark, uint8_t length);
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id);
+struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause,
+ uint8_t chosen_channel, uint8_t encr_alg_id,
+ uint8_t speech_mode);
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause);
+
+void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id);
+
+const struct tlv_definition *gsm0808_att_tlvdef();
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/gsm48.h b/src/shared/libosmocore/include/osmocore/gsm48.h
new file mode 100644
index 00000000..ffe0399b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm48.h
@@ -0,0 +1,36 @@
+#ifndef _OSMOCORE_GSM48_H
+#define _OSMOCORE_GSM48_H
+
+#include <osmocore/tlv.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/gsm48_ie.h>
+
+/* A parsed GPRS routing area */
+struct gprs_ra_id {
+ uint16_t mnc;
+ uint16_t mcc;
+ uint16_t lac;
+ uint8_t rac;
+};
+
+extern const struct tlv_definition gsm48_att_tlvdef;
+extern const struct tlv_definition gsm48_rr_att_tlvdef;
+extern const struct tlv_definition gsm48_mm_att_tlvdef;
+const char *gsm48_cc_state_name(uint8_t state);
+const char *gsm48_cc_msg_name(uint8_t msgtype);
+const char *rr_cause_name(uint8_t cause);
+
+void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
+ uint16_t mnc, uint16_t lac);
+int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
+int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len,
+ const uint8_t *mi, const int mi_len);
+
+/* Parse Routeing Area Identifier */
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/gsm48_ie.h b/src/shared/libosmocore/include/osmocore/gsm48_ie.h
new file mode 100644
index 00000000..200619a7
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm48_ie.h
@@ -0,0 +1,107 @@
+#ifndef _OSMOCORE_GSM48_IE_H
+#define _OSMOCORE_GSM48_IE_H
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/mncc.h>
+#include <osmocore/protocol/gsm_04_08.h>
+
+/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
+int gsm48_decode_bcd_number(char *output, int output_len,
+ const uint8_t *bcd_lv, int h_len);
+
+/* convert a ASCII phone number to 'called/calling/connect party BCD number' */
+int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
+ int h_len, const char *input);
+/* decode 'bearer capability' */
+int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
+ const uint8_t *lv);
+/* encode 'bearer capability' */
+int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_bearer_cap *bcap);
+/* decode 'call control cap' */
+int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv);
+/* encode 'call control cap' */
+int gsm48_encode_cccap(struct msgb *msg,
+ const struct gsm_mncc_cccap *ccap);
+/* decode 'called party BCD number' */
+int gsm48_decode_called(struct gsm_mncc_number *called,
+ const uint8_t *lv);
+/* encode 'called party BCD number' */
+int gsm48_encode_called(struct msgb *msg,
+ const struct gsm_mncc_number *called);
+/* decode callerid of various IEs */
+int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
+ const uint8_t *lv);
+/* encode callerid of various IEs */
+int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
+ const struct gsm_mncc_number *callerid);
+/* decode 'cause' */
+int gsm48_decode_cause(struct gsm_mncc_cause *cause,
+ const uint8_t *lv);
+/* encode 'cause' */
+int gsm48_encode_cause(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_cause *cause);
+/* decode 'calling number' */
+int gsm48_decode_calling(struct gsm_mncc_number *calling,
+ const uint8_t *lv);
+/* encode 'calling number' */
+int gsm48_encode_calling(struct msgb *msg,
+ const struct gsm_mncc_number *calling);
+/* decode 'connected number' */
+int gsm48_decode_connected(struct gsm_mncc_number *connected,
+ const uint8_t *lv);
+/* encode 'connected number' */
+int gsm48_encode_connected(struct msgb *msg,
+ const struct gsm_mncc_number *connected);
+/* decode 'redirecting number' */
+int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
+ const uint8_t *lv);
+/* encode 'redirecting number' */
+int gsm48_encode_redirecting(struct msgb *msg,
+ const struct gsm_mncc_number *redirecting);
+/* decode 'facility' */
+int gsm48_decode_facility(struct gsm_mncc_facility *facility,
+ const uint8_t *lv);
+/* encode 'facility' */
+int gsm48_encode_facility(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_facility *facility);
+/* decode 'notify' */
+int gsm48_decode_notify(int *notify, const uint8_t *v);
+/* encode 'notify' */
+int gsm48_encode_notify(struct msgb *msg, int notify);
+/* decode 'signal' */
+int gsm48_decode_signal(int *signal, const uint8_t *v);
+/* encode 'signal' */
+int gsm48_encode_signal(struct msgb *msg, int signal);
+/* decode 'keypad' */
+int gsm48_decode_keypad(int *keypad, const uint8_t *lv);
+/* encode 'keypad' */
+int gsm48_encode_keypad(struct msgb *msg, int keypad);
+/* decode 'progress' */
+int gsm48_decode_progress(struct gsm_mncc_progress *progress,
+ const uint8_t *lv);
+/* encode 'progress' */
+int gsm48_encode_progress(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_progress *p);
+/* decode 'user-user' */
+int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
+ const uint8_t *lv);
+/* encode 'useruser' */
+int gsm48_encode_useruser(struct msgb *msg, int lv_only,
+ const struct gsm_mncc_useruser *uu);
+/* decode 'ss version' */
+int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
+ const uint8_t *lv);
+/* encode 'ss version' */
+int gsm48_encode_ssversion(struct msgb *msg,
+ const struct gsm_mncc_ssversion *ssv);
+/* decode 'more data' does not require a function, because it has no value */
+/* encode 'more data' */
+int gsm48_encode_more(struct msgb *msg);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/gsm_utils.h b/src/shared/libosmocore/include/osmocore/gsm_utils.h
new file mode 100644
index 00000000..7dc2388b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm_utils.h
@@ -0,0 +1,117 @@
+/* GSM utility functions, e.g. coding and decoding */
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-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.
+ *
+ */
+
+#ifndef GSM_UTILS_H
+#define GSM_UTILS_H
+
+#include <stdint.h>
+
+#define ADD_MODULO(sum, delta, modulo) do { \
+ if ((sum += delta) >= modulo) \
+ sum -= modulo; \
+ } while (0)
+
+#define GSM_MAX_FN (26*51*2048)
+
+struct gsm_time {
+ uint32_t fn; /* FN count */
+ uint16_t t1; /* FN div (26*51) */
+ uint8_t t2; /* FN modulo 26 */
+ uint8_t t3; /* FN modulo 51 */
+ uint8_t tc;
+};
+
+enum gsm_band {
+ GSM_BAND_850 = 1,
+ GSM_BAND_900 = 2,
+ GSM_BAND_1800 = 4,
+ GSM_BAND_1900 = 8,
+ GSM_BAND_450 = 0x10,
+ GSM_BAND_480 = 0x20,
+ GSM_BAND_750 = 0x40,
+ GSM_BAND_810 = 0x80,
+};
+
+const char *gsm_band_name(enum gsm_band band);
+enum gsm_band gsm_band_parse(const char *mhz);
+
+int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
+int gsm_7bit_encode(uint8_t *result, const char *data);
+
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
+int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
+
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(uint8_t rxlev);
+uint8_t dbm2rxlev(int dbm);
+
+/* According to GSM 04.08 Chapter 10.5.1.6 */
+static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) {
+ switch (n) {
+ case 0: return 1;
+ case 1: return (cm2[0] & (1<<3)) ? 0 : 1;
+ case 2: return (cm2[2] & (1<<0)) ? 1 : 0;
+ case 3: return (cm2[2] & (1<<1)) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+/* According to GSM 04.08 Chapter 10.5.2.29 */
+static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
+static inline int rach_max_trans_raw2val(int raw) {
+ const int tbl[4] = { 1, 2, 4, 7 };
+ return tbl[raw & 3];
+}
+
+#define ARFCN_PCS 0x8000
+#define ARFCN_UPLINK 0x4000
+
+enum gsm_band gsm_arfcn2band(uint16_t arfcn);
+
+/* Convert an ARFCN to the frequency in MHz * 10 */
+uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink);
+
+/* Convert from frame number to GSM time */
+void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
+
+/* Convert from GSM time to frame number */
+uint32_t gsm_gsmtime2fn(struct gsm_time *time);
+
+/* GSM TS 03.03 Chapter 2.6 */
+enum gprs_tlli_type {
+ TLLI_LOCAL,
+ TLLI_FOREIGN,
+ TLLI_RANDOM,
+ TLLI_AUXILIARY,
+ TLLI_RESERVED,
+};
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli);
+
+uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type);
+
+void generate_backtrace();
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/gsmtap.h b/src/shared/libosmocore/include/osmocore/gsmtap.h
new file mode 100644
index 00000000..dcd64bdf
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsmtap.h
@@ -0,0 +1,72 @@
+#ifndef _GSMTAP_H
+#define _GSMTAP_H
+
+/* gsmtap header, pseudo-header in front of the actua GSM payload */
+
+/* GSMTAP is a generic header format for GSM protocol captures,
+ * it uses the IANA-assigned UDP port number 4729 and carries
+ * payload in various formats of GSM interfaces such as Um MAC
+ * blocks or Um bursts.
+ *
+ * Example programs generating GSMTAP data are airprobe
+ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/)
+ */
+
+#include <stdint.h>
+
+#define GSMTAP_VERSION 0x02
+
+#define GSMTAP_TYPE_UM 0x01
+#define GSMTAP_TYPE_ABIS 0x02
+#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */
+
+#define GSMTAP_BURST_UNKNOWN 0x00
+#define GSMTAP_BURST_FCCH 0x01
+#define GSMTAP_BURST_PARTIAL_SCH 0x02
+#define GSMTAP_BURST_SCH 0x03
+#define GSMTAP_BURST_CTS_SCH 0x04
+#define GSMTAP_BURST_COMPACT_SCH 0x05
+#define GSMTAP_BURST_NORMAL 0x06
+#define GSMTAP_BURST_DUMMY 0x07
+#define GSMTAP_BURST_ACCESS 0x08
+#define GSMTAP_BURST_NONE 0x09
+
+#define GSMTAP_CHANNEL_UNKNOWN 0x00
+#define GSMTAP_CHANNEL_BCCH 0x01
+#define GSMTAP_CHANNEL_CCCH 0x02
+#define GSMTAP_CHANNEL_RACH 0x03
+#define GSMTAP_CHANNEL_AGCH 0x04
+#define GSMTAP_CHANNEL_PCH 0x05
+#define GSMTAP_CHANNEL_SDCCH 0x06
+#define GSMTAP_CHANNEL_SDCCH4 0x07
+#define GSMTAP_CHANNEL_SDCCH8 0x08
+#define GSMTAP_CHANNEL_TCH_F 0x09
+#define GSMTAP_CHANNEL_TCH_H 0x0a
+#define GSMTAP_CHANNEL_ACCH 0x80
+
+#define GSMTAP_ARFCN_F_PCS 0x8000
+#define GSMTAP_ARFCN_F_UPLINK 0x4000
+#define GSMTAP_ARFCN_MASK 0x3fff
+
+#define GSMTAP_UDP_PORT 4729
+
+struct gsmtap_hdr {
+ uint8_t version; /* version, set to 0x01 currently */
+ uint8_t hdr_len; /* length in number of 32bit words */
+ uint8_t type; /* see GSMTAP_TYPE_* */
+ uint8_t timeslot; /* timeslot (0..7 on Um) */
+
+ uint16_t arfcn; /* ARFCN (frequency) */
+ int8_t signal_dbm; /* signal level in dBm */
+ int8_t snr_db; /* signal/noise ratio in dB */
+
+ uint32_t frame_number; /* GSM Frame Number (FN) */
+
+ uint8_t sub_type; /* Type of burst/channel, see above */
+ uint8_t antenna_nr; /* Antenna Number */
+ uint8_t sub_slot; /* sub-slot within timeslot */
+ uint8_t res; /* reserved for future use (RFU) */
+
+} __attribute__((packed));
+
+#endif /* _GSMTAP_H */
diff --git a/src/shared/libosmocore/include/osmocore/gsmtap_util.h b/src/shared/libosmocore/include/osmocore/gsmtap_util.h
new file mode 100644
index 00000000..96449443
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsmtap_util.h
@@ -0,0 +1,21 @@
+#ifndef _GSMTAP_UTIL_H
+#define _GSMTAP_UTIL_H
+
+#include <stdint.h>
+
+/* convert RSL channel number to GSMTAP channel type */
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
+ uint8_t ss, uint32_t fn, int8_t signal_dbm,
+ uint8_t snr, const uint8_t *data, unsigned int len);
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss,
+ uint32_t fn, int8_t signal_dbm, uint8_t snr,
+ const uint8_t *data, unsigned int len);
+
+int gsmtap_init(uint32_t dst_ip);
+
+#endif /* _GSMTAP_UTIL_H */
diff --git a/src/shared/libosmocore/include/osmocore/linuxlist.h b/src/shared/libosmocore/include/osmocore/linuxlist.h
new file mode 100644
index 00000000..fb99c5ec
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/linuxlist.h
@@ -0,0 +1,360 @@
+#ifndef _LINUX_LLIST_H
+#define _LINUX_LLIST_H
+
+#include <stddef.h>
+
+#ifndef inline
+#define inline __inline__
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
+ (type *)( (char *)__mptr - offsetof(type, member) );})
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1 ((void *) 0x00100100)
+#define LLIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked llist implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct llist_head {
+ struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LLIST_HEAD(name) \
+ struct llist_head name = LLIST_HEAD_INIT(name)
+
+#define INIT_LLIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *_new,
+ struct llist_head *prev,
+ struct llist_head *next)
+{
+ next->prev = _new;
+ _new->next = next;
+ _new->prev = prev;
+ prev->next = _new;
+}
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head, head->next);
+}
+
+/**
+ * llist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * llist_del - deletes entry from llist.
+ * @entry: the element to delete from the llist.
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ entry->next = (struct llist_head *)LLIST_POISON1;
+ entry->prev = (struct llist_head *)LLIST_POISON2;
+}
+
+/**
+ * llist_del_init - deletes entry from llist and reinitialize it.
+ * @entry: the element to delete from the llist.
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ INIT_LLIST_HEAD(entry);
+}
+
+/**
+ * llist_move - delete from one llist and add as another's head
+ * @llist: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add(llist, head);
+}
+
+/**
+ * llist_move_tail - delete from one llist and add as another's tail
+ * @llist: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+ struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add_tail(llist, head);
+}
+
+/**
+ * llist_empty - tests whether a llist is empty
+ * @head: the llist to test.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+ struct llist_head *head)
+{
+ struct llist_head *first = llist->next;
+ struct llist_head *last = llist->prev;
+ struct llist_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * llist_splice - join two llists
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+ if (!llist_empty(llist))
+ __llist_splice(llist, head);
+}
+
+/**
+ * llist_splice_init - join two llists and reinitialise the emptied llist.
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+ struct llist_head *head)
+{
+ if (!llist_empty(llist)) {
+ __llist_splice(llist, head);
+ INIT_LLIST_HEAD(llist);
+ }
+}
+
+/**
+ * llist_entry - get the struct for this entry
+ * @ptr: the &struct llist_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * llist_for_each_prev - iterate over a llist backwards
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * llist_for_each_safe - iterate over a llist safe against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * llist_for_each_entry - iterate over llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_reverse - iterate backwards over llist of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_reverse(pos, head, member) \
+ for (pos = llist_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/**
+ * llist_for_each_entry_continue - iterate over llist of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_continue(pos, head, member) \
+ for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ n = llist_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu - iterate over an rcu-protected llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
+ * against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu - iterate over rcu llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu - iterate over an rcu-protected llist
+ * continuing after existing point.
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/logging.h b/src/shared/libosmocore/include/osmocore/logging.h
new file mode 100644
index 00000000..2e82959a
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/logging.h
@@ -0,0 +1,135 @@
+#ifndef _OSMOCORE_LOGGING_H
+#define _OSMOCORE_LOGGING_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocore/linuxlist.h>
+
+#define LOG_MAX_CATEGORY 32
+#define LOG_MAX_CTX 8
+#define LOG_MAX_FILTERS 8
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
+#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
+#else
+#define DEBUGP(xss, fmt, args...)
+#define DEBUGPC(ss, fmt, args...)
+#endif
+
+#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+char *hexdump(const unsigned char *buf, int len);
+void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
+
+/* new logging interface */
+#define LOGP(ss, level, fmt, args...) \
+ logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
+#define LOGPC(ss, level, fmt, args...) \
+ logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
+
+/* different levels */
+#define LOGL_DEBUG 1 /* debugging information */
+#define LOGL_INFO 3
+#define LOGL_NOTICE 5 /* abnormal/unexpected condition */
+#define LOGL_ERROR 7 /* error condition, requires user action */
+#define LOGL_FATAL 8 /* fatal, program aborted */
+
+#define LOG_FILTER_ALL 0x0001
+
+struct log_category {
+ uint8_t loglevel;
+ uint8_t enabled;
+};
+
+struct log_info_cat {
+ const char *name;
+ const char *color;
+ const char *description;
+ uint8_t loglevel;
+ uint8_t enabled;
+};
+
+/* log context information, passed to filter */
+struct log_context {
+ void *ctx[LOG_MAX_CTX+1];
+};
+
+struct log_target;
+
+typedef int log_filter(const struct log_context *ctx,
+ struct log_target *target);
+
+struct log_info {
+ /* filter callback function */
+ log_filter *filter_fn;
+
+ /* per-category information */
+ const struct log_info_cat *cat;
+ unsigned int num_cat;
+};
+
+struct log_target {
+ struct llist_head entry;
+
+ int filter_map;
+ void *filter_data[LOG_MAX_FILTERS+1];
+
+ struct log_category categories[LOG_MAX_CATEGORY+1];
+ uint8_t loglevel;
+ int use_color:1;
+ int print_timestamp:1;
+
+ union {
+ struct {
+ FILE *out;
+ } tgt_stdout;
+
+ struct {
+ int priority;
+ } tgt_syslog;
+
+ struct {
+ void *vty;
+ } tgt_vty;
+ };
+
+ void (*output) (struct log_target *target, const char *string);
+};
+
+/* use the above macros */
+void logp2(unsigned int subsys, unsigned int level, char *file,
+ int line, int cont, const char *format, ...)
+ __attribute__ ((format (printf, 6, 7)));
+void log_init(const struct log_info *cat);
+
+/* context management */
+void log_reset_context(void);
+int log_set_context(uint8_t ctx, void *value);
+
+/* filter on the targets */
+void log_set_all_filter(struct log_target *target, int);
+
+void log_set_use_color(struct log_target *target, int);
+void log_set_print_timestamp(struct log_target *target, int);
+void log_set_log_level(struct log_target *target, int log_level);
+void log_parse_category_mask(struct log_target *target, const char* mask);
+int log_parse_level(const char *lvl);
+const char *log_level_str(unsigned int lvl);
+int log_parse_category(const char *category);
+void log_set_category_filter(struct log_target *target, int category,
+ int enable, int level);
+
+/* management of the targets */
+struct log_target *log_target_create(void);
+struct log_target *log_target_create_stderr(void);
+void log_add_target(struct log_target *target);
+void log_del_target(struct log_target *target);
+
+/* Gernerate command argument strings for VTY use */
+const char *log_vty_category_string(struct log_info *info);
+const char *log_vty_level_string(struct log_info *info);
+
+#endif /* _OSMOCORE_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocore/mncc.h b/src/shared/libosmocore/include/osmocore/mncc.h
new file mode 100644
index 00000000..a094bb9b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/mncc.h
@@ -0,0 +1,71 @@
+#ifndef _OSMOCORE_MNCC_H
+#define _OSMOCORE_MNCC_H
+
+#define GSM_MAX_FACILITY 128
+#define GSM_MAX_SSVERSION 128
+#define GSM_MAX_USERUSER 128
+
+/* Expanded fields from GSM TS 04.08, Table 10.5.102 */
+struct gsm_mncc_bearer_cap {
+ int transfer; /* Information Transfer Capability */
+ int mode; /* Transfer Mode */
+ int coding; /* Coding Standard */
+ int radio; /* Radio Channel Requirement */
+ int speech_ctm; /* CTM text telephony indication */
+ int speech_ver[8]; /* Speech version indication */
+};
+
+struct gsm_mncc_number {
+ int type;
+ int plan;
+ int present;
+ int screen;
+ char number[33];
+};
+
+struct gsm_mncc_cause {
+ int location;
+ int coding;
+ int rec;
+ int rec_val;
+ int value;
+ int diag_len;
+ char diag[32];
+};
+
+struct gsm_mncc_useruser {
+ int proto;
+ char info[GSM_MAX_USERUSER + 1]; /* + termination char */
+};
+
+struct gsm_mncc_progress {
+ int coding;
+ int location;
+ int descr;
+};
+
+struct gsm_mncc_facility {
+ int len;
+ char info[GSM_MAX_FACILITY];
+};
+
+struct gsm_mncc_ssversion {
+ int len;
+ char info[GSM_MAX_SSVERSION];
+};
+
+struct gsm_mncc_cccap {
+ int dtmf;
+ int pcp;
+};
+
+enum {
+ GSM_MNCC_BCAP_SPEECH = 0,
+ GSM_MNCC_BCAP_UNR_DIG = 1,
+ GSM_MNCC_BCAP_AUDIO = 2,
+ GSM_MNCC_BCAP_FAX_G3 = 3,
+ GSM_MNCC_BCAP_OTHER_ITC = 5,
+ GSM_MNCC_BCAP_RESERVED = 7,
+};
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/msgb.h b/src/shared/libosmocore/include/osmocore/msgb.h
new file mode 100644
index 00000000..2841dc56
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/msgb.h
@@ -0,0 +1,165 @@
+#ifndef _MSGB_H
+#define _MSGB_H
+
+/* (C) 2008 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 "linuxlist.h"
+
+struct msgb {
+ struct llist_head list;
+
+ /* Part of which TRX logical channel we were received / transmitted */
+ /* FIXME: move them into the control buffer */
+ struct gsm_bts_trx *trx;
+ struct gsm_lchan *lchan;
+
+ /* the Layer1 header (if any) */
+ unsigned char *l1h;
+ /* the A-bis layer 2 header: OML, RSL(RLL), NS */
+ unsigned char *l2h;
+ /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
+ unsigned char *l3h;
+ /* the layer 4 header */
+ unsigned char *l4h;
+
+ /* the 'control buffer', large enough to contain 5 pointers */
+ unsigned long cb[5];
+
+ uint16_t data_len;
+ uint16_t len;
+
+ unsigned char *head;
+ unsigned char *tail;
+ unsigned char *data;
+ unsigned char _data[0];
+};
+
+extern struct msgb *msgb_alloc(uint16_t size, const char *name);
+extern void msgb_free(struct msgb *m);
+extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
+extern struct msgb *msgb_dequeue(struct llist_head *queue);
+extern void msgb_reset(struct msgb *m);
+
+#define msgb_l1(m) ((void *)(m->l1h))
+#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l3(m) ((void *)(m->l3h))
+#define msgb_sms(m) ((void *)(m->l4h))
+
+static inline unsigned int msgb_l1len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l1(msgb);
+}
+
+static inline unsigned int msgb_l2len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l2(msgb);
+}
+
+static inline unsigned int msgb_l3len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l3(msgb);
+}
+
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+ return msgb->len - msgb->data_len;
+}
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+ msgb->tail += len;
+ msgb->len += len;
+ return tmp;
+}
+static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
+{
+ uint8_t *space = msgb_put(msgb, 1);
+ space[0] = word & 0xFF;
+}
+static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
+{
+ uint8_t *space = msgb_put(msgb, 2);
+ space[0] = word >> 8 & 0xFF;
+ space[1] = word & 0xFF;
+}
+static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
+{
+ uint8_t *space = msgb_put(msgb, 4);
+ space[0] = word >> 24 & 0xFF;
+ space[1] = word >> 16 & 0xFF;
+ space[2] = word >> 8 & 0xFF;
+ space[3] = word & 0xFF;
+}
+static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->data;
+ msgb->data += len;
+ msgb->len -= len;
+ return tmp;
+}
+static inline uint8_t msgb_get_u8(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 1);
+ return space[0];
+}
+static inline uint16_t msgb_get_u16(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 2);
+ return space[0] << 8 | space[1];
+}
+static inline uint32_t msgb_get_u32(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 4);
+ return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3];
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+ msgb->data -= len;
+ msgb->len += len;
+ return msgb->data;
+}
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+ msgb->len -= len;
+ return msgb->data += len;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->head + msgb->data_len) - msgb->tail;
+}
+
+/* increase the headroom of an empty msgb, reducing the tailroom */
+static inline void msgb_reserve(struct msgb *msg, int len)
+{
+ msg->data += len;
+ msg->tail += len;
+}
+
+static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+ const char *name)
+{
+ struct msgb *msg = msgb_alloc(size, name);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+#endif /* _MSGB_H */
diff --git a/src/shared/libosmocore/include/osmocore/plugin.h b/src/shared/libosmocore/include/osmocore/plugin.h
new file mode 100644
index 00000000..98f9b56d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/plugin.h
@@ -0,0 +1,6 @@
+#ifndef _OSMO_PLUGIN_H
+#define _OSMO_PLUGIN_H
+
+int plugin_load_all(const char *directory);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/protocol/Makefile.am b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am
new file mode 100644
index 00000000..557950ec
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am
@@ -0,0 +1,3 @@
+osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h
+
+osmocore_protodir = $(includedir)/osmocore/protocol
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
new file mode 100644
index 00000000..80a455dd
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
@@ -0,0 +1,1243 @@
+#ifndef PROTO_GSM_04_08_H
+#define PROTO_GSM_04_08_H
+
+#include <stdint.h>
+
+/* GSM TS 04.08 definitions */
+struct gsm_lchan;
+
+/* Chapter 10.5.1.5 */
+struct gsm48_classmark1 {
+ uint8_t pwr_lev:3,
+ a5_1:1,
+ es_ind:1,
+ rev_lev:2,
+ spare:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.6 */
+struct gsm48_classmark2 {
+ uint8_t pwr_lev:3,
+ a5_1:1,
+ es_ind:1,
+ rev_lev:2,
+ spare:1;
+ uint8_t fc:1,
+ vgcs:1,
+ vbs:1,
+ sm_cap:1,
+ ss_scr:2,
+ ps_cap:1,
+ spare2:1;
+ uint8_t a5_2:1,
+ a5_3:1,
+ cmsp:1,
+ solsa:1,
+ spare3:1,
+ lcsva_cap:1,
+ spare4:1,
+ cm3:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.3 */
+struct gsm48_range_1024 {
+ uint8_t w1_hi:2,
+ f0:1,
+ form_id:5;
+ uint8_t w1_lo;
+ uint8_t w2_hi;
+ uint8_t w3_hi:7,
+ w2_lo:1;
+ uint8_t w4_hi:6,
+ w3_lo:2;
+ uint8_t w5_hi:6,
+ w4_lo:2;
+ uint8_t w6_hi:6,
+ w5_lo:2;
+ uint8_t w7_hi:6,
+ w6_lo:2;
+ uint8_t w8_hi:6,
+ w7_lo:2;
+ uint8_t w9:7,
+ w8_lo:1;
+ uint8_t w11_hi:1,
+ w10:7;
+ uint8_t w12_hi:2,
+ w11_lo:6;
+ uint8_t w13_hi:3,
+ w12_lo:5;
+ uint8_t w14_hi:4,
+ w13_lo:4;
+ uint8_t w15_hi:5,
+ w14_lo:3;
+ uint8_t w16:6,
+ w15_lo:2;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.4 */
+struct gsm48_range_512 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1_hi:7,
+ orig_arfcn_lo:1;
+ uint8_t w2_hi:6,
+ w1_lo:2;
+ uint8_t w3_hi:6,
+ w2_lo:2;
+ uint8_t w4_hi:6,
+ w3_lo:2;
+ uint8_t w5:7,
+ w4_lo:1;
+ uint8_t w7_hi:1,
+ w6:7;
+ uint8_t w8_hi:2,
+ w7_lo:6;
+ uint8_t w9_hi:4,
+ w8_lo:4;
+ uint8_t w10:6,
+ w9_lo:2;
+ uint8_t w12_hi:2,
+ w11:6;
+ uint8_t w13_hi:4,
+ w12_lo:4;
+ uint8_t w14:6,
+ w13_lo:2;
+ uint8_t w16_hi:2,
+ w15:6;
+ uint8_t w17:5,
+ w16_lo:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.5 */
+struct gsm48_range_256 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1_hi:7,
+ orig_arfcn_lo:1;
+ uint8_t w2:7,
+ w1_lo:1;
+ uint8_t w4_hi:1,
+ w3:7;
+ uint8_t w5_hi:3,
+ w4_lo:5;
+ uint8_t w6_hi:5,
+ w5_lo:3;
+ uint8_t w8_hi:1,
+ w7:6,
+ w6_lo:1;
+ uint8_t w9_hi:4,
+ w8_lo:4;
+ uint8_t w11_hi:2,
+ w10:5,
+ w9_lo:1;
+ uint8_t w12:5,
+ w11_lo:3;
+ uint8_t w14_hi:3,
+ w13:5;
+ uint8_t w16_hi:1,
+ w15:5,
+ w14_lo:2;
+ uint8_t w18_hi:1,
+ w17:4,
+ w16_lo:3;
+ uint8_t w20_hi:1,
+ w19:4,
+ w18_lo:3;
+ uint8_t spare:1,
+ w21:4,
+ w20_lo:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.6 */
+struct gsm48_range_128 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1:7,
+ orig_arfcn_lo:1;
+ uint8_t w3_hi:2,
+ w2:6;
+ uint8_t w4_hi:4,
+ w3_lo:4;
+ uint8_t w6_hi:2,
+ w5:5,
+ w4_lo:1;
+ uint8_t w7:5,
+ w6_lo:3;
+ uint8_t w9:4,
+ w8:4;
+ uint8_t w11:4,
+ w10:4;
+ uint8_t w13:4,
+ w12:4;
+ uint8_t w15:4,
+ w14:4;
+ uint8_t w18_hi:2,
+ w17:3,
+ w16:3;
+ uint8_t w21_hi:1,
+ w20:3,
+ w19:3,
+ w18_lo:1;
+ uint8_t w23:3,
+ w22:3,
+ w21_lo:2;
+ uint8_t w26_hi:2,
+ w25:3,
+ w24:3;
+ uint8_t spare:1,
+ w28:3,
+ w27:3,
+ w26_lo:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.7 */
+struct gsm48_var_bit {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t rrfcn1_7:7,
+ orig_arfcn_lo:1;
+ uint8_t rrfcn8_111[13];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.5 */
+struct gsm48_chan_desc {
+ uint8_t chan_nr;
+ union {
+ struct {
+ uint8_t maio_high:4,
+ h:1,
+ tsc:3;
+ uint8_t hsn:6,
+ maio_low:2;
+ } h1;
+ struct {
+ uint8_t arfcn_high:2,
+ spare:2,
+ h:1,
+ tsc:3;
+ uint8_t arfcn_low;
+ } h0;
+ };
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.20 */
+struct gsm48_meas_res {
+ uint8_t rxlev_full:6,
+ dtx_used:1,
+ ba_used:1;
+ uint8_t rxlev_sub:6,
+ meas_valid:1,
+ spare:1;
+ uint8_t no_nc_n_hi:1,
+ rxqual_sub:3,
+ rxqual_full:3,
+ spare2:1;
+ uint8_t rxlev_nc1:6,
+ no_nc_n_lo:2;
+ uint8_t bsic_nc1_hi:3,
+ bcch_f_nc1:5;
+ uint8_t rxlev_nc2_hi:5,
+ bsic_nc1_lo:3;
+ uint8_t bsic_nc2_hi:2,
+ bcch_f_nc2:5,
+ rxlev_nc2_lo:1;
+ uint8_t rxlev_nc3_hi:4,
+ bsic_nc2_lo:4;
+ uint8_t bsic_nc3_hi:1,
+ bcch_f_nc3:5,
+ rxlev_nc3_lo:2;
+ uint8_t rxlev_nc4_hi:3,
+ bsic_nc3_lo:5;
+ uint8_t bcch_f_nc4:5,
+ rxlev_nc4_lo:3;
+ uint8_t rxlev_nc5_hi:2,
+ bsic_nc4:6;
+ uint8_t bcch_f_nc5_hi:4,
+ rxlev_nc5_lo:4;
+ uint8_t rxlev_nc6_hi:1,
+ bsic_nc5:6,
+ bcch_f_nc5_lo:1;
+ uint8_t bcch_f_nc6_hi:3,
+ rxlev_nc6_lo:5;
+ uint8_t bsic_nc6:6,
+ bcch_f_nc6_lo:2;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.21aa */
+struct gsm48_multi_rate_conf {
+ uint8_t smod : 2,
+ spare: 1,
+ icmi : 1,
+ nscb : 1,
+ ver : 3;
+ uint8_t m4_75 : 1,
+ m5_15 : 1,
+ m5_90 : 1,
+ m6_70 : 1,
+ m7_40 : 1,
+ m7_95 : 1,
+ m10_2 : 1,
+ m12_2 : 1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.28(a) */
+struct gsm48_power_cmd {
+ uint8_t power_level:5,
+ spare:2,
+ atc:1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.29 */
+struct gsm48_rach_control {
+ uint8_t re :1,
+ cell_bar :1,
+ tx_integer :4,
+ max_trans :2;
+ uint8_t t2;
+ uint8_t t3;
+} __attribute__ ((packed));
+
+
+/* Chapter 10.5.2.30 */
+struct gsm48_req_ref {
+ uint8_t ra;
+ uint8_t t3_high:3,
+ t1:5;
+ uint8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.38 */
+struct gsm48_start_time {
+ uint8_t t3_high:3,
+ t1:5;
+ uint8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.39 */
+struct gsm48_sync_ind {
+ uint8_t si:2,
+ rot:1,
+ nci:1,
+ sync_ie:4;
+} __attribute__((packed));
+
+/*
+ * Chapter 9.1.5/9.1.6
+ *
+ * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a
+ */
+struct gsm48_chan_mode_modify {
+ struct gsm48_chan_desc chan_desc;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+enum gsm48_chan_mode {
+ GSM48_CMODE_SIGN = 0x00,
+ GSM48_CMODE_SPEECH_V1 = 0x01,
+ GSM48_CMODE_SPEECH_EFR = 0x21,
+ GSM48_CMODE_SPEECH_AMR = 0x41,
+ GSM48_CMODE_DATA_14k5 = 0x0f,
+ GSM48_CMODE_DATA_12k0 = 0x03,
+ GSM48_CMODE_DATA_6k0 = 0x0b,
+ GSM48_CMODE_DATA_3k6 = 0x23,
+};
+
+/* Chapter 9.1.2 */
+struct gsm48_ass_cmd {
+ /* Semantic is from 10.5.2.5a */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t power_command;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 10.5.2.2 */
+struct gsm48_cell_desc {
+ uint8_t bcc:3,
+ ncc:3,
+ arfcn_hi:2;
+ uint8_t arfcn_lo;
+} __attribute__((packed));
+
+/* Chapter 9.1.15 */
+struct gsm48_ho_cmd {
+ struct gsm48_cell_desc cell_desc;
+ struct gsm48_chan_desc chan_desc;
+ uint8_t ho_ref;
+ uint8_t power_command;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.1.18 */
+struct gsm48_imm_ass {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_chan_desc chan_desc;
+ struct gsm48_req_ref req_ref;
+ uint8_t timing_advance;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Chapter 9.1.25 */
+struct gsm48_pag_resp {
+ uint8_t spare:4,
+ key_seq:4;
+ uint32_t classmark2;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.3 */
+struct gsm48_loc_area_id {
+ uint8_t digits[3]; /* BCD! */
+ uint16_t lac;
+} __attribute__ ((packed));
+
+/* Section 9.2.2 */
+struct gsm48_auth_req {
+ uint8_t key_seq:4,
+ spare:4;
+ uint8_t rand[16];
+} __attribute__ ((packed));
+
+/* Section 9.2.3 */
+struct gsm48_auth_resp {
+ uint8_t sres[4];
+} __attribute__ ((packed));
+
+/* Section 9.2.15 */
+struct gsm48_loc_upd_req {
+ uint8_t type:4,
+ key_seq:4;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_classmark1 classmark1;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.1 */
+struct gsm48_hdr {
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.3x System information Type header */
+struct gsm48_system_information_type_header {
+ uint8_t l2_plen;
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.4 Cell Selection Parameters */
+struct gsm48_cell_sel_par {
+ uint8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */
+ cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */
+ uint8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */
+ neci:1,
+ acs:1;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
+struct gsm48_control_channel_descr {
+ uint8_t ccch_conf :3,
+ bs_ag_blks_res :3,
+ att :1,
+ spare1 :1;
+ uint8_t bs_pa_mfrms : 3,
+ spare2 :5;
+ uint8_t t3212;
+} __attribute__ ((packed));
+
+struct gsm48_cell_options {
+ uint8_t radio_link_timeout:4,
+ dtx:2,
+ pwrc:1,
+ spare:1;
+} __attribute__ ((packed));
+
+/* Section 9.2.9 CM service request */
+struct gsm48_service_request {
+ uint8_t cm_service_type : 4,
+ cipher_key_seq : 4;
+ /* length + 3 bytes */
+ uint32_t classmark;
+ uint8_t mi_len;
+ uint8_t mi[0];
+ /* optional priority level */
+} __attribute__ ((packed));
+
+/* Section 9.1.31 System information Type 1 */
+struct gsm48_system_information_type_1 {
+ struct gsm48_system_information_type_header header;
+ uint8_t cell_channel_description[16];
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0]; /* NCH position on the CCCH */
+} __attribute__ ((packed));
+
+/* Section 9.1.32 System information Type 2 */
+struct gsm48_system_information_type_2 {
+ struct gsm48_system_information_type_header header;
+ uint8_t bcch_frequency_list[16];
+ uint8_t ncc_permitted;
+ struct gsm48_rach_control rach_control;
+} __attribute__ ((packed));
+
+/* Section 9.1.33 System information Type 2bis */
+struct gsm48_system_information_type_2bis {
+ struct gsm48_system_information_type_header header;
+ uint8_t bcch_frequency_list[16];
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.34 System information Type 2ter */
+struct gsm48_system_information_type_2ter {
+ struct gsm48_system_information_type_header header;
+ uint8_t ext_bcch_frequency_list[16];
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.35 System information Type 3 */
+struct gsm48_system_information_type_3 {
+ struct gsm48_system_information_type_header header;
+ uint16_t cell_identity;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_control_channel_descr control_channel_desc;
+ struct gsm48_cell_options cell_options;
+ struct gsm48_cell_sel_par cell_sel_par;
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.36 System information Type 4 */
+struct gsm48_system_information_type_4 {
+ struct gsm48_system_information_type_header header;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_cell_sel_par cell_sel_par;
+ struct gsm48_rach_control rach_control;
+ /* optional CBCH conditional CBCH... followed by
+ mandantory SI 4 Reset Octets
+ */
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.37 System information Type 5 */
+struct gsm48_system_information_type_5 {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.38 System information Type 5bis */
+struct gsm48_system_information_type_5bis {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.39 System information Type 5ter */
+struct gsm48_system_information_type_5ter {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.40 System information Type 6 */
+struct gsm48_system_information_type_6 {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint16_t cell_identity;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_cell_options cell_options;
+ uint8_t ncc_permitted;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.43a System Information type 13 */
+struct gsm48_system_information_type_13 {
+ struct gsm48_system_information_type_header header;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.2.12 IMSI Detach Indication */
+struct gsm48_imsi_detach_ind {
+ struct gsm48_classmark1 classmark1;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.1 */
+struct gsm48_add_ass {
+ /* Semantic is from 10.5.2.5 */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.3 */
+struct gsm48_ass_cpl {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.4 */
+struct gsm48_ass_fail {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.7 */
+struct gsm48_chan_rel {
+ uint8_t rr_cause;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.9 */
+struct gsm48_cip_mode_cmd {
+ uint8_t sc:1,
+ alg_id:3,
+ spare:3,
+ cr:1;
+} __attribute__((packed));
+
+/* Section 9.1.11 */
+struct gsm48_cm_change {
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.19 */
+struct gsm48_imm_ass_ext {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_chan_desc chan_desc1;
+ struct gsm48_req_ref req_ref1;
+ uint8_t timing_advance1;
+ struct gsm48_chan_desc chan_desc2;
+ struct gsm48_req_ref req_ref2;
+ uint8_t timing_advance2;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.20 */
+struct gsm48_imm_ass_rej {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_req_ref req_ref1;
+ uint8_t wait_ind1;
+ struct gsm48_req_ref req_ref2;
+ uint8_t wait_ind2;
+ struct gsm48_req_ref req_ref3;
+ uint8_t wait_ind3;
+ struct gsm48_req_ref req_ref4;
+ uint8_t wait_ind4;
+ uint8_t rest[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.22 */
+struct gsm48_paging1 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.23 */
+struct gsm48_paging2 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint32_t tmsi1;
+ uint32_t tmsi2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.24 */
+struct gsm48_paging3 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint32_t tmsi1;
+ uint32_t tmsi2;
+ uint32_t tmsi3;
+ uint32_t tmsi4;
+ uint8_t cneed3:2,
+ cneed4:2,
+ spare2:4;
+ uint8_t rest[0];
+} __attribute__((packed));
+
+/* Section 9.1.25 */
+struct gsm48_pag_rsp {
+ uint8_t key_seq:3,
+ spare:5;
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.29 */
+struct gsm48_rr_status {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 10.2 + GSM 04.07 12.2.3.1.1 */
+#define GSM48_PDISC_GROUP_CC 0x00
+#define GSM48_PDISC_BCAST_CC 0x01
+#define GSM48_PDISC_PDSS1 0x02
+#define GSM48_PDISC_CC 0x03
+#define GSM48_PDISC_PDSS2 0x04
+#define GSM48_PDISC_MM 0x05
+#define GSM48_PDISC_RR 0x06
+#define GSM48_PDISC_MM_GPRS 0x08
+#define GSM48_PDISC_SMS 0x09
+#define GSM48_PDISC_SM_GPRS 0x0a
+#define GSM48_PDISC_NC_SS 0x0b
+#define GSM48_PDISC_LOC 0x0c
+#define GSM48_PDISC_MASK 0x0f
+#define GSM48_PDISC_USSD 0x11
+
+/* Section 10.4 */
+#define GSM48_MT_RR_INIT_REQ 0x3c
+#define GSM48_MT_RR_ADD_ASS 0x3b
+#define GSM48_MT_RR_IMM_ASS 0x3f
+#define GSM48_MT_RR_IMM_ASS_EXT 0x39
+#define GSM48_MT_RR_IMM_ASS_REJ 0x3a
+
+#define GSM48_MT_RR_CIPH_M_CMD 0x35
+#define GSM48_MT_RR_CIPH_M_COMPL 0x32
+
+#define GSM48_MT_RR_CFG_CHG_CMD 0x30
+#define GSM48_MT_RR_CFG_CHG_ACK 0x31
+#define GSM48_MT_RR_CFG_CHG_REJ 0x33
+
+#define GSM48_MT_RR_ASS_CMD 0x2e
+#define GSM48_MT_RR_ASS_COMPL 0x29
+#define GSM48_MT_RR_ASS_FAIL 0x2f
+#define GSM48_MT_RR_HANDO_CMD 0x2b
+#define GSM48_MT_RR_HANDO_COMPL 0x2c
+#define GSM48_MT_RR_HANDO_FAIL 0x28
+#define GSM48_MT_RR_HANDO_INFO 0x2d
+
+#define GSM48_MT_RR_CELL_CHG_ORDER 0x08
+#define GSM48_MT_RR_PDCH_ASS_CMD 0x23
+
+#define GSM48_MT_RR_CHAN_REL 0x0d
+#define GSM48_MT_RR_PART_REL 0x0a
+#define GSM48_MT_RR_PART_REL_COMP 0x0f
+
+#define GSM48_MT_RR_PAG_REQ_1 0x21
+#define GSM48_MT_RR_PAG_REQ_2 0x22
+#define GSM48_MT_RR_PAG_REQ_3 0x24
+#define GSM48_MT_RR_PAG_RESP 0x27
+#define GSM48_MT_RR_NOTIF_NCH 0x20
+#define GSM48_MT_RR_NOTIF_FACCH 0x25
+#define GSM48_MT_RR_NOTIF_RESP 0x26
+
+#define GSM48_MT_RR_SYSINFO_8 0x18
+#define GSM48_MT_RR_SYSINFO_1 0x19
+#define GSM48_MT_RR_SYSINFO_2 0x1a
+#define GSM48_MT_RR_SYSINFO_3 0x1b
+#define GSM48_MT_RR_SYSINFO_4 0x1c
+#define GSM48_MT_RR_SYSINFO_5 0x1d
+#define GSM48_MT_RR_SYSINFO_6 0x1e
+#define GSM48_MT_RR_SYSINFO_7 0x1f
+
+#define GSM48_MT_RR_SYSINFO_2bis 0x02
+#define GSM48_MT_RR_SYSINFO_2ter 0x03
+#define GSM48_MT_RR_SYSINFO_5bis 0x05
+#define GSM48_MT_RR_SYSINFO_5ter 0x06
+#define GSM48_MT_RR_SYSINFO_9 0x04
+#define GSM48_MT_RR_SYSINFO_13 0x00
+
+#define GSM48_MT_RR_SYSINFO_16 0x3d
+#define GSM48_MT_RR_SYSINFO_17 0x3e
+
+#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10
+#define GSM48_MT_RR_STATUS 0x12
+#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17
+#define GSM48_MT_RR_FREQ_REDEF 0x14
+#define GSM48_MT_RR_MEAS_REP 0x15
+#define GSM48_MT_RR_CLSM_CHG 0x16
+#define GSM48_MT_RR_CLSM_ENQ 0x13
+#define GSM48_MT_RR_EXT_MEAS_REP 0x36
+#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37
+#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34
+
+#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08
+#define GSM48_MT_RR_UPLINK_RELEASE 0x0e
+#define GSM48_MT_RR_UPLINK_FREE 0x0c
+#define GSM48_MT_RR_UPLINK_BUSY 0x2a
+#define GSM48_MT_RR_TALKER_IND 0x11
+
+#define GSM48_MT_RR_APP_INFO 0x38
+
+/* Table 10.2/3GPP TS 04.08 */
+#define GSM48_MT_MM_IMSI_DETACH_IND 0x01
+#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
+#define GSM48_MT_MM_LOC_UPD_REJECT 0x04
+#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08
+
+#define GSM48_MT_MM_AUTH_REJ 0x11
+#define GSM48_MT_MM_AUTH_REQ 0x12
+#define GSM48_MT_MM_AUTH_RESP 0x14
+#define GSM48_MT_MM_ID_REQ 0x18
+#define GSM48_MT_MM_ID_RESP 0x19
+#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a
+#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b
+
+#define GSM48_MT_MM_CM_SERV_ACC 0x21
+#define GSM48_MT_MM_CM_SERV_REJ 0x22
+#define GSM48_MT_MM_CM_SERV_ABORT 0x23
+#define GSM48_MT_MM_CM_SERV_REQ 0x24
+#define GSM48_MT_MM_CM_SERV_PROMPT 0x25
+#define GSM48_MT_MM_CM_REEST_REQ 0x28
+#define GSM48_MT_MM_ABORT 0x29
+
+#define GSM48_MT_MM_NULL 0x30
+#define GSM48_MT_MM_STATUS 0x31
+#define GSM48_MT_MM_INFO 0x32
+
+/* Table 10.3/3GPP TS 04.08 */
+#define GSM48_MT_CC_ALERTING 0x01
+#define GSM48_MT_CC_CALL_CONF 0x08
+#define GSM48_MT_CC_CALL_PROC 0x02
+#define GSM48_MT_CC_CONNECT 0x07
+#define GSM48_MT_CC_CONNECT_ACK 0x0f
+#define GSM48_MT_CC_EMERG_SETUP 0x0e
+#define GSM48_MT_CC_PROGRESS 0x03
+#define GSM48_MT_CC_ESTAB 0x04
+#define GSM48_MT_CC_ESTAB_CONF 0x06
+#define GSM48_MT_CC_RECALL 0x0b
+#define GSM48_MT_CC_START_CC 0x09
+#define GSM48_MT_CC_SETUP 0x05
+
+#define GSM48_MT_CC_MODIFY 0x17
+#define GSM48_MT_CC_MODIFY_COMPL 0x1f
+#define GSM48_MT_CC_MODIFY_REJECT 0x13
+#define GSM48_MT_CC_USER_INFO 0x10
+#define GSM48_MT_CC_HOLD 0x18
+#define GSM48_MT_CC_HOLD_ACK 0x19
+#define GSM48_MT_CC_HOLD_REJ 0x1a
+#define GSM48_MT_CC_RETR 0x1c
+#define GSM48_MT_CC_RETR_ACK 0x1d
+#define GSM48_MT_CC_RETR_REJ 0x1e
+
+#define GSM48_MT_CC_DISCONNECT 0x25
+#define GSM48_MT_CC_RELEASE 0x2d
+#define GSM48_MT_CC_RELEASE_COMPL 0x2a
+
+#define GSM48_MT_CC_CONG_CTRL 0x39
+#define GSM48_MT_CC_NOTIFY 0x3e
+#define GSM48_MT_CC_STATUS 0x3d
+#define GSM48_MT_CC_STATUS_ENQ 0x34
+#define GSM48_MT_CC_START_DTMF 0x35
+#define GSM48_MT_CC_STOP_DTMF 0x31
+#define GSM48_MT_CC_STOP_DTMF_ACK 0x32
+#define GSM48_MT_CC_START_DTMF_ACK 0x36
+#define GSM48_MT_CC_START_DTMF_REJ 0x37
+#define GSM48_MT_CC_FACILITY 0x3a
+
+/* FIXME: Table 10.4 / 10.4a (GPRS) */
+
+/* Section 10.5.3.3 CM service type */
+#define GSM48_CMSERV_MO_CALL_PACKET 1
+#define GSM48_CMSERV_EMERGENCY 2
+#define GSM48_CMSERV_SMS 4
+#define GSM48_CMSERV_SUP_SERV 8
+#define GSM48_CMSERV_VGCS 9
+#define GSM48_CMSERV_VBS 10
+#define GSM48_CMSERV_LOC_SERV 11
+
+/* Section 10.5.2.26, Table 10.5.64 */
+#define GSM48_PM_MASK 0x03
+#define GSM48_PM_NORMAL 0x00
+#define GSM48_PM_EXTENDED 0x01
+#define GSM48_PM_REORG 0x02
+#define GSM48_PM_SAME 0x03
+
+/* Chapter 10.5.3.5 / Table 10.5.93 */
+#define GSM48_LUPD_NORMAL 0x0
+#define GSM48_LUPD_PERIODIC 0x1
+#define GSM48_LUPD_IMSI_ATT 0x2
+#define GSM48_LUPD_RESERVED 0x3
+
+/* Table 10.5.4 */
+#define GSM_MI_TYPE_MASK 0x07
+#define GSM_MI_TYPE_NONE 0x00
+#define GSM_MI_TYPE_IMSI 0x01
+#define GSM_MI_TYPE_IMEI 0x02
+#define GSM_MI_TYPE_IMEISV 0x03
+#define GSM_MI_TYPE_TMSI 0x04
+#define GSM_MI_ODD 0x08
+
+#define GSM48_IE_MOBILE_ID 0x17 /* 10.5.1.4 */
+#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */
+#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */
+#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */
+#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */
+#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */
+
+#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */
+#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */
+#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */
+#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */
+#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */
+#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */
+#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */
+#define GSM48_IE_NOTIFY 0x27 /* 10.5.4.20 */
+#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */
+#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */
+#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */
+#define GSM48_IE_CONN_SUB 0x4d /* 10.5.4.14 */
+#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */
+#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */
+#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */
+#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */
+#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */
+#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */
+#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */
+#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */
+#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */
+#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */
+#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */
+#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */
+#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */
+#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */
+#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */
+#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */
+
+/* Section 10.5.4.11 / Table 10.5.122 */
+#define GSM48_CAUSE_CS_GSM 0x60
+
+/* Section 9.1.2 / Table 9.3 */
+/* RR elements */
+#define GSM48_IE_VGCS_TARGET 0x01
+//#define GSM48_IE_VGCS_T_MODE_I 0x01
+#define GSM48_IE_FRQSHORT_AFTER 0x02
+#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */
+#define GSM48_IE_FREQ_L_AFTER 0x05
+#define GSM48_IE_MSLOT_DESC 0x10
+#define GSM48_IE_CHANMODE_2 0x11
+#define GSM48_IE_FRQSHORT_BEFORE 0x12
+//#define GSM48_IE_FRQSHORT_BEFOR 0x12
+#define GSM48_IE_CHANMODE_3 0x13
+#define GSM48_IE_CHANMODE_4 0x14
+#define GSM48_IE_CHANMODE_5 0x15
+#define GSM48_IE_CHANMODE_6 0x16
+#define GSM48_IE_CHANMODE_7 0x17
+#define GSM48_IE_CHANMODE_8 0x18
+#define GSM48_IE_CHANDESC_2 0x64
+#define GSM48_IE_MA_AFTER 0x72
+#define GSM48_IE_START_TIME 0x7c
+#define GSM48_IE_FREQ_L_BEFORE 0x19
+//#define GSM48_IE_FRQLIST_BEFORE 0x19
+#define GSM48_IE_CH_DESC_1_BEFORE 0x1c
+//#define GSM48_IE_CHDES_1_BEFORE 0x1c
+#define GSM48_IE_CH_DESC_2_BEFORE 0x1d
+//#define GSM48_IE_CHDES_2_BEFORE 0x1d
+#define GSM48_IE_F_CH_SEQ_BEFORE 0x1e
+//#define GSM48_IE_FRQSEQ_BEFORE 0x1e
+#define GSM48_IE_CLASSMARK3 0x20
+#define GSM48_IE_MA_BEFORE 0x21
+#define GSM48_IE_RR_PACKET_UL 0x22
+#define GSM48_IE_RR_PACKET_DL 0x23
+#define GSM48_IE_CELL_CH_DESC 0x62
+#define GSM48_IE_CHANMODE_1 0x63
+#define GSM48_IE_CHDES_2_AFTER 0x64
+#define GSM48_IE_MODE_SEC_CH 0x66
+#define GSM48_IE_F_CH_SEQ_AFTER 0x69
+#define GSM48_IE_MA_AFTER 0x72
+#define GSM48_IE_BA_RANGE 0x73
+#define GSM48_IE_GROUP_CHDES 0x74
+#define GSM48_IE_BA_LIST_PREF 0x75
+#define GSM48_IE_MOB_OVSERV_DIF 0x77
+#define GSM48_IE_REALTIME_DIFF 0x7b
+#define GSM48_IE_START_TIME 0x7c
+#define GSM48_IE_TIMING_ADVANCE 0x7d
+#define GSM48_IE_GROUP_CIP_SEQ 0x80
+#define GSM48_IE_CIP_MODE_SET 0x90
+#define GSM48_IE_GPRS_RESUMPT 0xc0
+#define GSM48_IE_SYNC_IND 0xd0
+/* System Information 4 (types are equal IEs above) */
+#define GSM48_IE_CBCH_CHAN_DESC 0x64
+#define GSM48_IE_CBCH_MOB_AL 0x72
+
+/* Additional MM elements */
+#define GSM48_IE_LOCATION_AREA 0x13
+#define GSM48_IE_PRIORITY_LEV 0x80
+#define GSM48_IE_FOLLOW_ON_PROC 0xa1
+#define GSM48_IE_CTS_PERMISSION 0xa2
+
+/* Section 10.5.4.23 / Table 10.5.130 */
+enum gsm48_signal_val {
+ GSM48_SIGNAL_DIALTONE = 0x00,
+ GSM48_SIGNAL_RINGBACK = 0x01,
+ GSM48_SIGNAL_INTERCEPT = 0x02,
+ GSM48_SIGNAL_NET_CONG = 0x03,
+ GSM48_SIGNAL_BUSY = 0x04,
+ GSM48_SIGNAL_CONFIRM = 0x05,
+ GSM48_SIGNAL_ANSWER = 0x06,
+ GSM48_SIGNAL_CALL_WAIT = 0x07,
+ GSM48_SIGNAL_OFF_HOOK = 0x08,
+ GSM48_SIGNAL_OFF = 0x3f,
+ GSM48_SIGNAL_ALERT_OFF = 0x4f,
+};
+
+enum gsm48_cause_loc {
+ GSM48_CAUSE_LOC_USER = 0x00,
+ GSM48_CAUSE_LOC_PRN_S_LU = 0x01,
+ GSM48_CAUSE_LOC_PUN_S_LU = 0x02,
+ GSM48_CAUSE_LOC_TRANS_NET = 0x03,
+ GSM48_CAUSE_LOC_PUN_S_RU = 0x04,
+ GSM48_CAUSE_LOC_PRN_S_RU = 0x05,
+ /* not defined */
+ GSM48_CAUSE_LOC_INN_NET = 0x07,
+ GSM48_CAUSE_LOC_NET_BEYOND = 0x0a,
+};
+
+/* Section 10.5.2.31 RR Cause / Table 10.5.70 */
+enum gsm48_rr_cause {
+ GSM48_RR_CAUSE_NORMAL = 0x00,
+ GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01,
+ GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02,
+ GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
+ GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
+ GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
+ GSM48_RR_CAUSE_HNDOVER_IMP = 0x06,
+ GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07,
+ GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08,
+ GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
+ GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
+ GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
+ GSM48_RR_CAUSE_MSG_TYPE_N = 0x61,
+ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62,
+ GSM48_RR_CAUSE_COND_IE_ERROR = 0x64,
+ GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
+};
+
+/* Section 10.5.4.11 CC Cause / Table 10.5.123 */
+enum gsm48_cc_cause {
+ GSM48_CC_CAUSE_UNASSIGNED_NR = 1,
+ GSM48_CC_CAUSE_NO_ROUTE = 3,
+ GSM48_CC_CAUSE_CHAN_UNACCEPT = 6,
+ GSM48_CC_CAUSE_OP_DET_BARRING = 8,
+ GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16,
+ GSM48_CC_CAUSE_USER_BUSY = 17,
+ GSM48_CC_CAUSE_USER_NOTRESPOND = 18,
+ GSM48_CC_CAUSE_USER_ALERTING_NA = 19,
+ GSM48_CC_CAUSE_CALL_REJECTED = 21,
+ GSM48_CC_CAUSE_NUMBER_CHANGED = 22,
+ GSM48_CC_CAUSE_PRE_EMPTION = 25,
+ GSM48_CC_CAUSE_NONSE_USER_CLR = 26,
+ GSM48_CC_CAUSE_DEST_OOO = 27,
+ GSM48_CC_CAUSE_INV_NR_FORMAT = 28,
+ GSM48_CC_CAUSE_FACILITY_REJ = 29,
+ GSM48_CC_CAUSE_RESP_STATUS_INQ = 30,
+ GSM48_CC_CAUSE_NORMAL_UNSPEC = 31,
+ GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34,
+ GSM48_CC_CAUSE_NETWORK_OOO = 38,
+ GSM48_CC_CAUSE_TEMP_FAILURE = 41,
+ GSM48_CC_CAUSE_SWITCH_CONG = 42,
+ GSM48_CC_CAUSE_ACC_INF_DISCARD = 43,
+ GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47,
+ GSM48_CC_CAUSE_QOS_UNAVAIL = 49,
+ GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50,
+ GSM48_CC_CAUSE_INC_BARRED_CUG = 55,
+ GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58,
+ GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63,
+ GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65,
+ GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68,
+ GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69,
+ GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID = 81,
+ GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87,
+ GSM48_CC_CAUSE_INCOMPAT_DEST = 88,
+ GSM48_CC_CAUSE_INVAL_TRANS_NET = 91,
+ GSM48_CC_CAUSE_SEMANTIC_INCORR = 95,
+ GSM48_CC_CAUSE_INVAL_MAND_INF = 96,
+ GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97,
+ GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98,
+ GSM48_CC_CAUSE_IE_NOTEXIST = 99,
+ GSM48_CC_CAUSE_COND_IE_ERR = 100,
+ GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101,
+ GSM48_CC_CAUSE_RECOVERY_TIMER = 102,
+ GSM48_CC_CAUSE_PROTO_ERR = 111,
+ GSM48_CC_CAUSE_INTERWORKING = 127,
+};
+
+/* Annex G, GSM specific cause values for mobility management */
+enum gsm48_reject_value {
+ GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2,
+ GSM48_REJECT_ILLEGAL_MS = 3,
+ GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4,
+ GSM48_REJECT_IMEI_NOT_ACCEPTED = 5,
+ GSM48_REJECT_ILLEGAL_ME = 6,
+ GSM48_REJECT_PLMN_NOT_ALLOWED = 11,
+ GSM48_REJECT_LOC_NOT_ALLOWED = 12,
+ GSM48_REJECT_ROAMING_NOT_ALLOWED = 13,
+ GSM48_REJECT_NETWORK_FAILURE = 17,
+ GSM48_REJECT_CONGESTION = 22,
+ GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32,
+ GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33,
+ GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34,
+ GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38,
+ GSM48_REJECT_INCORRECT_MESSAGE = 95,
+ GSM48_REJECT_INVALID_MANDANTORY_INF = 96,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97,
+ GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98,
+ GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99,
+ GSM48_REJECT_CONDTIONAL_IE_ERROR = 100,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE = 101,
+ GSM48_REJECT_PROTOCOL_ERROR = 111,
+
+ /* according to G.6 Additional cause codes for GMM */
+ GSM48_REJECT_GPRS_NOT_ALLOWED = 7,
+ GSM48_REJECT_SERVICES_NOT_ALLOWED = 8,
+ GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9,
+ GSM48_REJECT_IMPLICITLY_DETACHED = 10,
+ GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14,
+ GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16,
+};
+
+enum chreq_type {
+ CHREQ_T_EMERG_CALL,
+ CHREQ_T_CALL_REEST_TCH_F,
+ CHREQ_T_CALL_REEST_TCH_H,
+ CHREQ_T_CALL_REEST_TCH_H_DBL,
+ CHREQ_T_SDCCH,
+ CHREQ_T_TCH_F,
+ CHREQ_T_VOICE_CALL_TCH_H,
+ CHREQ_T_DATA_CALL_TCH_H,
+ CHREQ_T_LOCATION_UPD,
+ CHREQ_T_PAG_R_ANY_NECI0,
+ CHREQ_T_PAG_R_ANY_NECI1,
+ CHREQ_T_PAG_R_TCH_F,
+ CHREQ_T_PAG_R_TCH_FH,
+ CHREQ_T_LMU,
+ CHREQ_T_RESERVED_SDCCH,
+ CHREQ_T_RESERVED_IGNORE,
+};
+
+/* Chapter 11.3 */
+#define GSM48_T301 180, 0
+#define GSM48_T303 30, 0
+#define GSM48_T305 30, 0
+#define GSM48_T306 30, 0
+#define GSM48_T308 10, 0
+#define GSM48_T310 180, 0
+#define GSM48_T313 30, 0
+#define GSM48_T323 30, 0
+#define GSM48_T331 30, 0
+#define GSM48_T333 30, 0
+#define GSM48_T334 25, 0 /* min 15 */
+#define GSM48_T338 30, 0
+#define GSM48_T303_MS 30, 0
+#define GSM48_T305_MS 30, 0
+#define GSM48_T308_MS 30, 0
+#define GSM48_T310_MS 30, 0
+#define GSM48_T313_MS 30, 0
+#define GSM48_T323_MS 30, 0
+#define GSM48_T332_MS 30, 0
+#define GSM48_T335_MS 30, 0
+
+/* Chapter 5.1.2.2 */
+#define GSM_CSTATE_NULL 0
+#define GSM_CSTATE_INITIATED 1
+#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */
+#define GSM_CSTATE_MO_CALL_PROC 3
+#define GSM_CSTATE_CALL_DELIVERED 4
+#define GSM_CSTATE_CALL_PRESENT 6
+#define GSM_CSTATE_CALL_RECEIVED 7
+#define GSM_CSTATE_CONNECT_REQUEST 8
+#define GSM_CSTATE_MO_TERM_CALL_CONF 9
+#define GSM_CSTATE_ACTIVE 10
+#define GSM_CSTATE_DISCONNECT_REQ 12
+#define GSM_CSTATE_DISCONNECT_IND 12
+#define GSM_CSTATE_RELEASE_REQ 19
+#define GSM_CSTATE_MO_ORIG_MODIFY 26
+#define GSM_CSTATE_MO_TERM_MODIFY 27
+#define GSM_CSTATE_CONNECT_IND 28
+
+#define SBIT(a) (1 << a)
+#define ALL_STATES 0xffffffff
+
+/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */
+#define GSM_LAC_RESERVED_DETACHED 0x0
+#define GSM_LAC_RESERVED_ALL_BTS 0xfffe
+
+/* GSM 04.08 Bearer Capability: Information Transfer Capability */
+enum gsm48_bcap_itcap {
+ GSM48_BCAP_ITCAP_SPEECH = 0,
+ GSM48_BCAP_ITCAP_UNR_DIG_INF = 1,
+ GSM48_BCAP_ITCAP_3k1_AUDIO = 2,
+ GSM48_BCAP_ITCAP_FAX_G3 = 3,
+ GSM48_BCAP_ITCAP_OTHER = 5,
+ GSM48_BCAP_ITCAP_RESERVED = 7,
+};
+
+/* GSM 04.08 Bearer Capability: Transfer Mode */
+enum gsm48_bcap_tmod {
+ GSM48_BCAP_TMOD_CIRCUIT = 0,
+ GSM48_BCAP_TMOD_PACKET = 1,
+};
+
+/* GSM 04.08 Bearer Capability: Coding Standard */
+enum gsm48_bcap_coding {
+ GSM48_BCAP_CODING_GSM_STD = 0,
+};
+
+/* GSM 04.08 Bearer Capability: Radio Channel Requirements */
+enum gsm48_bcap_rrq {
+ GSM48_BCAP_RRQ_FR_ONLY = 1,
+ GSM48_BCAP_RRQ_DUAL_HR = 2,
+ GSM48_BCAP_RRQ_DUAL_FR = 3,
+};
+
+#define GSM48_TMSI_LEN 5
+#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2)
+#define GSM48_MI_SIZE 32
+
+/* Chapter 10.4.4.15 */
+struct gsm48_ra_id {
+ uint8_t digits[3]; /* MCC + MNC BCD digits */
+ uint16_t lac; /* Location Area Code */
+ uint8_t rac; /* Routing Area Code */
+} __attribute__ ((packed));
+
+
+
+#endif /* PROTO_GSM_04_08_H */
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h
new file mode 100644
index 00000000..c6a2b193
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h
@@ -0,0 +1,188 @@
+#ifndef PROTO_GSM_04_11_H
+#define PROTO_GSM_04_11_H
+
+#include <stdint.h>
+
+/* GSM TS 04.11 definitions */
+
+/* Chapter 5.2.3: SMC-CS states at the network side */
+enum gsm411_cp_state {
+ GSM411_CPS_IDLE = 0,
+ GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */
+ GSM411_CPS_WAIT_CP_ACK = 2,
+ GSM411_CPS_MM_ESTABLISHED = 3,
+};
+
+/* Chapter 6.2.2: SMR states at the network side */
+enum gsm411_rp_state {
+ GSM411_RPS_IDLE = 0,
+ GSM411_RPS_WAIT_FOR_RP_ACK = 1,
+ GSM411_RPS_WAIT_TO_TX_RP_ACK = 3,
+};
+
+/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */
+#define GSM411_PDISC_SMS 0x09
+
+/* Chapter 8.1.3 */
+#define GSM411_MT_CP_DATA 0x01
+#define GSM411_MT_CP_ACK 0x04
+#define GSM411_MT_CP_ERROR 0x10
+
+enum gsm411_cp_ie {
+ GSM411_CP_IE_USER_DATA = 0x01, /* 8.1.4.1 */
+ GSM411_CP_IE_CAUSE = 0x02, /* 8.1.4.2. */
+};
+
+/* Section 8.1.4.2 / Table 8.2 */
+enum gsm411_cp_cause {
+ GSM411_CP_CAUSE_NET_FAIL = 17,
+ GSM411_CP_CAUSE_CONGESTION = 22,
+ GSM411_CP_CAUSE_INV_TRANS_ID = 81,
+ GSM411_CP_CAUSE_SEMANT_INC_MSG = 95,
+ GSM411_CP_CAUSE_INV_MAND_INF = 96,
+ GSM411_CP_CAUSE_MSGTYPE_NOTEXIST= 97,
+ GSM411_CP_CAUSE_MSG_INCOMP_STATE= 98,
+ GSM411_CP_CAUSE_IE_NOTEXIST = 99,
+ GSM411_CP_CAUSE_PROTOCOL_ERR = 111,
+};
+
+/* Chapter 8.2.2 */
+#define GSM411_MT_RP_DATA_MO 0x00
+#define GSM411_MT_RP_DATA_MT 0x01
+#define GSM411_MT_RP_ACK_MO 0x02
+#define GSM411_MT_RP_ACK_MT 0x03
+#define GSM411_MT_RP_ERROR_MO 0x04
+#define GSM411_MT_RP_ERROR_MT 0x05
+#define GSM411_MT_RP_SMMA_MO 0x06
+
+enum gsm411_rp_ie {
+ GSM411_IE_RP_USER_DATA = 0x41, /* 8.2.5.3 */
+ GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */
+};
+
+/* Chapter 8.2.5.4 Table 8.4 */
+enum gsm411_rp_cause {
+ /* valid only for MO */
+ GSM411_RP_CAUSE_MO_NUM_UNASSIGNED = 1,
+ GSM411_RP_CAUSE_MO_OP_DET_BARR = 8,
+ GSM411_RP_CAUSE_MO_CALL_BARRED = 10,
+ GSM411_RP_CAUSE_MO_SMS_REJECTED = 21,
+ GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER = 27,
+ GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR = 28,
+ GSM411_RP_CAUSE_MO_FACILITY_REJ = 29,
+ GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR = 30,
+ GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER = 38,
+ GSM411_RP_CAUSE_MO_TEMP_FAIL = 41,
+ GSM411_RP_CAUSE_MO_CONGESTION = 42,
+ GSM411_RP_CAUSE_MO_RES_UNAVAIL = 47,
+ GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR = 50,
+ GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL = 69,
+ GSM411_RP_CAUSE_MO_INTERWORKING = 127,
+ /* valid only for MT */
+ GSM411_RP_CAUSE_MT_MEM_EXCEEDED = 22,
+ /* valid for both directions */
+ GSM411_RP_CAUSE_INV_TRANS_REF = 81,
+ GSM411_RP_CAUSE_SEMANT_INC_MSG = 95,
+ GSM411_RP_CAUSE_INV_MAND_INF = 96,
+ GSM411_RP_CAUSE_MSGTYPE_NOTEXIST = 97,
+ GSM411_RP_CAUSE_MSG_INCOMP_STATE = 98,
+ GSM411_RP_CAUSE_IE_NOTEXIST = 99,
+ GSM411_RP_CAUSE_PROTOCOL_ERR = 111,
+};
+
+/* Chapter 10: Timers */
+#define GSM411_TMR_TR1M 40, 0 /* 35 < x < 45 seconds */
+#define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */
+#define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */
+
+#define GSM411_TMR_TC1A 30, 0
+
+/* Chapter 8.2.1 */
+struct gsm411_rp_hdr {
+ uint8_t len;
+ uint8_t msg_type;
+ uint8_t msg_ref;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* our own enum, not related to on-air protocol */
+enum sms_alphabet {
+ DCS_NONE,
+ DCS_7BIT_DEFAULT,
+ DCS_UCS2,
+ DCS_8BIT_DATA,
+};
+
+/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */
+#define GSM340_SMS_DELIVER_SC2MS 0x00
+#define GSM340_SMS_DELIVER_REP_MS2SC 0x00
+#define GSM340_SMS_STATUS_REP_SC2MS 0x02
+#define GSM340_SMS_COMMAND_MS2SC 0x02
+#define GSM340_SMS_SUBMIT_MS2SC 0x01
+#define GSM340_SMS_SUBMIT_REP_SC2MS 0x01
+#define GSM340_SMS_RESSERVED 0x03
+
+/* GSM 03.40 / Chapter 9.2.3.2: TP-More-Messages-to-Send */
+#define GSM340_TP_MMS_MORE 0
+#define GSM340_TP_MMS_NO_MORE 1
+
+/* GSM 03.40 / Chapter 9.2.3.3: TP-Validity-Period-Format */
+#define GSM340_TP_VPF_NONE 0
+#define GSM340_TP_VPF_RELATIVE 2
+#define GSM340_TP_VPF_ENHANCED 1
+#define GSM340_TP_VPF_ABSOLUTE 3
+
+/* GSM 03.40 / Chapter 9.2.3.4: TP-Status-Report-Indication */
+#define GSM340_TP_SRI_NONE 0
+#define GSM340_TP_SRI_PRESENT 1
+
+/* GSM 03.40 / Chapter 9.2.3.5: TP-Status-Report-Request */
+#define GSM340_TP_SRR_NONE 0
+#define GSM340_TP_SRR_REQUESTED 1
+
+/* GSM 03.40 / Chapter 9.2.3.9: TP-Protocol-Identifier */
+/* telematic interworking (001 or 111 in bits 7-5) */
+#define GSM340_TP_PID_IMPLICIT 0x00
+#define GSM340_TP_PID_TELEX 0x01
+#define GSM340_TP_PID_FAX_G3 0x02
+#define GSM340_TP_PID_FAX_G4 0x03
+#define GSM340_TP_PID_VOICE 0x04
+#define GSM430_TP_PID_ERMES 0x05
+#define GSM430_TP_PID_NATIONAL_PAGING 0x06
+#define GSM430_TP_PID_VIDEOTEX 0x07
+#define GSM430_TP_PID_TELETEX_UNSPEC 0x08
+#define GSM430_TP_PID_TELETEX_PSPDN 0x09
+#define GSM430_TP_PID_TELETEX_CSPDN 0x0a
+#define GSM430_TP_PID_TELETEX_PSTN 0x0b
+#define GSM430_TP_PID_TELETEX_ISDN 0x0c
+#define GSM430_TP_PID_TELETEX_UCI 0x0d
+#define GSM430_TP_PID_MSG_HANDLING 0x10
+#define GSM430_TP_PID_MSG_X400 0x11
+#define GSM430_TP_PID_EMAIL 0x12
+#define GSM430_TP_PID_GSM_MS 0x1f
+/* if bit 7 = 0 and bit 6 = 1 */
+#define GSM430_TP_PID_SMS_TYPE_0 0
+#define GSM430_TP_PID_SMS_TYPE_1 1
+#define GSM430_TP_PID_SMS_TYPE_2 2
+#define GSM430_TP_PID_SMS_TYPE_3 3
+#define GSM430_TP_PID_SMS_TYPE_4 4
+#define GSM430_TP_PID_SMS_TYPE_5 5
+#define GSM430_TP_PID_SMS_TYPE_6 6
+#define GSM430_TP_PID_SMS_TYPE_7 7
+#define GSM430_TP_PID_RETURN_CALL_MSG 0x1f
+#define GSM430_TP_PID_ME_DATA_DNLOAD 0x3d
+#define GSM430_TP_PID_ME_DE_PERSONAL 0x3e
+#define GSM430_TP_PID_ME_SIM_DNLOAD 0x3f
+
+/* GSM 03.38 Chapter 4: SMS Data Coding Scheme */
+#define GSM338_DCS_00_
+
+#define GSM338_DCS_1110_7BIT (0 << 2)
+#define GSM338_DCS_1111_7BIT (0 << 2)
+#define GSM338_DCS_1111_8BIT_DATA (1 << 2)
+#define GSM338_DCS_1111_CLASS0 0
+#define GSM338_DCS_1111_CLASS1_ME 1
+#define GSM338_DCS_1111_CLASS2_SIM 2
+#define GSM338_DCS_1111_CLASS3_TE 3 /* See TS 07.05 */
+
+#endif /* PROTO_GSM_04_11_H */
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h
new file mode 100644
index 00000000..fa5c9451
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h
@@ -0,0 +1,126 @@
+#ifndef PROTO_GSM_04_80_H
+#define PROTO_GSM_04_80_H
+
+/* GSM TS 04.80 definitions (Supplementary Services Specification, Formats and Coding) */
+
+/* Section 3.4 */
+#define GSM0480_MTYPE_RELEASE_COMPLETE 0x2A
+#define GSM0480_MTYPE_FACILITY 0x3A
+#define GSM0480_MTYPE_REGISTER 0x3B
+
+/* Section 3.5 */
+#define GSM0480_IE_FACILITY 0x1C
+#define GSM0480_IE_SS_VERSION 0x7F
+
+/* Section 3.6.2 */
+#define GSM0480_CTYPE_INVOKE 0xA1
+#define GSM0480_CTYPE_RETURN_RESULT 0xA2
+#define GSM0480_CTYPE_RETURN_ERROR 0xA3
+#define GSM0480_CTYPE_REJECT 0xA4
+
+/* Section 3.6.3 */
+#define GSM0480_COMPIDTAG_INVOKE_ID 0x02
+#define GSM0480_COMPIDTAG_LINKED_ID 0x80
+
+/* Section 3.6.4 */
+#define GSM0480_OPERATION_CODE 0x02
+
+/* Section 3.6.5 */
+#define GSM_0480_SEQUENCE_TAG 0x30
+#define GSM_0480_SET_TAG 0x31
+
+/* Section 3.6.6 */
+#define GSM_0480_ERROR_CODE_TAG 0x02
+
+/* Section 3.6.7 */
+/* Table 3.13 */
+#define GSM_0480_PROBLEM_CODE_TAG_GENERAL 0x80
+#define GSM_0480_PROBLEM_CODE_TAG_INVOKE 0x81
+#define GSM_0480_PROBLEM_CODE_TAG_RETURN_RESULT 0x82
+#define GSM_0480_PROBLEM_CODE_TAG_RETURN_ERROR 0x83
+
+/* Table 3.14 */
+#define GSM_0480_GEN_PROB_CODE_UNRECOGNISED 0x00
+#define GSM_0480_GEN_PROB_CODE_MISTYPED 0x01
+#define GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE 0x02
+
+/* Table 3.15 */
+#define GSM_0480_INVOKE_PROB_CODE_DUPLICATE_INVOKE_ID 0x00
+#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_OPERATION 0x01
+#define GSM_0480_INVOKE_PROB_CODE_MISTYPED_PARAMETER 0x02
+#define GSM_0480_INVOKE_PROB_CODE_RESOURCE_LIMITATION 0x03
+#define GSM_0480_INVOKE_PROB_CODE_INITIATING_RELEASE 0x04
+#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_LINKED_ID 0x05
+#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_RESPONSE 0x06
+#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_OPERATION 0x07
+
+/* Table 3.16 */
+#define GSM_0480_RESULT_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00
+#define GSM_0480_RESULT_PROB_CODE_RETURN_RESULT_UNEXPECTED 0x01
+#define GSM_0480_RESULT_PROB_CODE_MISTYPED_PARAMETER 0x02
+
+/* Table 3.17 */
+#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00
+#define GSM_0480_ERROR_PROB_CODE_RETURN_ERROR_UNEXPECTED 0x01
+#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_ERROR 0x02
+#define GSM_0480_ERROR_PROB_CODE_UNEXPECTED_ERROR 0x03
+#define GSM_0480_ERROR_PROB_CODE_MISTYPED_PARAMETER 0x04
+
+/* Section 4.5 */
+#define GSM0480_OP_CODE_REGISTER_SS 0x0A
+#define GSM0480_OP_CODE_ERASE_SS 0x0B
+#define GSM0480_OP_CODE_ACTIVATE_SS 0x0C
+#define GSM0480_OP_CODE_DEACTIVATE_SS 0x0D
+#define GSM0480_OP_CODE_INTERROGATE_SS 0x0E
+#define GSM0480_OP_CODE_NOTIFY_SS 0x10
+#define GSM0480_OP_CODE_REGISTER_PASSWORD 0x11
+#define GSM0480_OP_CODE_GET_PASSWORD 0x12
+#define GSM0480_OP_CODE_PROCESS_USS_DATA 0x13
+#define GSM0480_OP_CODE_FORWARD_CHECK_SS_IND 0x26
+#define GSM0480_OP_CODE_PROCESS_USS_REQ 0x3B
+#define GSM0480_OP_CODE_USS_REQUEST 0x3C
+#define GSM0480_OP_CODE_USS_NOTIFY 0x3D
+#define GSM0480_OP_CODE_FORWARD_CUG_INFO 0x78
+#define GSM0480_OP_CODE_SPLIT_MPTY 0x79
+#define GSM0480_OP_CODE_RETRIEVE_MPTY 0x7A
+#define GSM0480_OP_CODE_HOLD_MPTY 0x7B
+#define GSM0480_OP_CODE_BUILD_MPTY 0x7C
+#define GSM0480_OP_CODE_FORWARD_CHARGE_ADVICE 0x7D
+
+#define GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER 0x01
+#define GSM0480_ERR_CODE_ILLEGAL_SUBSCRIBER 0x09
+#define GSM0480_ERR_CODE_BEARER_SERVICE_NOT_PROVISIONED 0x0A
+#define GSM0480_ERR_CODE_TELESERVICE_NOT_PROVISIONED 0x0B
+#define GSM0480_ERR_CODE_ILLEGAL_EQUIPMENT 0x0C
+#define GSM0480_ERR_CODE_CALL_BARRED 0x0D
+#define GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION 0x10
+#define GSM0480_ERR_CODE_SS_ERROR_STATUS 0x11
+#define GSM0480_ERR_CODE_SS_NOT_AVAILABLE 0x12
+#define GSM0480_ERR_CODE_SS_SUBSCRIPTION_VIOLATION 0x13
+#define GSM0480_ERR_CODE_SS_INCOMPATIBILITY 0x14
+#define GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED 0x15
+#define GSM0480_ERR_CODE_ABSENT_SUBSCRIBER 0x1B
+#define GSM0480_ERR_CODE_SYSTEM_FAILURE 0x22
+#define GSM0480_ERR_CODE_DATA_MISSING 0x23
+#define GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE 0x24
+#define GSM0480_ERR_CODE_PW_REGISTRATION_FAILURE 0x25
+#define GSM0480_ERR_CODE_NEGATIVE_PW_CHECK 0x26
+#define GSM0480_ERR_CODE_NUM_PW_ATTEMPTS_VIOLATION 0x2B
+#define GSM0480_ERR_CODE_UNKNOWN_ALPHABET 0x47
+#define GSM0480_ERR_CODE_USSD_BUSY 0x48
+#define GSM0480_ERR_CODE_MAX_MPTY_PARTICIPANTS 0x7E
+#define GSM0480_ERR_CODE_RESOURCES_NOT_AVAILABLE 0x7F
+
+/* ASN.1 type-tags */
+#define ASN1_BOOLEAN_TAG 0x01
+#define ASN1_INTEGER_TAG 0x02
+#define ASN1_BIT_STRING_TAG 0x03
+#define ASN1_OCTET_STRING_TAG 0x04
+#define ASN1_NULL_TYPE_TAG 0x05
+#define ASN1_OBJECT_ID_TAG 0x06
+#define ASN1_UTF8_STRING_TAG 0x0C
+#define ASN1_PRINTABLE_STRING_TAG 0x13
+#define ASN1_IA5_STRING_TAG 0x16
+#define ASN1_UNICODE_STRING_TAG 0x1E
+
+#endif /* PROTO_GSM_04_80_H */
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h
new file mode 100644
index 00000000..6b8f9359
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h
@@ -0,0 +1,303 @@
+/* From GSM08.08 */
+
+#ifndef GSM_0808_H
+#define GSM_0808_H
+
+#include <stdlib.h>
+
+/*
+ * this is from GSM 03.03 CGI but is copied in GSM 08.08
+ * in § 3.2.2.27 for Cell Identifier List
+ */
+enum CELL_IDENT {
+ CELL_IDENT_WHOLE_GLOBAL = 0,
+ CELL_IDENT_LAC_AND_CI = 1,
+ CELL_IDENT_CI = 2,
+ CELL_IDENT_NO_CELL = 3,
+ CELL_IDENT_LAI_AND_LAC = 4,
+ CELL_IDENT_LAC = 5,
+ CELL_IDENT_BSS = 6,
+ CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
+ CELL_IDENT_UTRAN_RNC = 9,
+ CELL_IDENT_UTRAN_LAC_RNC = 10,
+};
+
+
+/* GSM 08.06 § 6.3 */
+enum BSSAP_MSG_TYPE {
+ BSSAP_MSG_BSS_MANAGEMENT = 0x0,
+ BSSAP_MSG_DTAP = 0x1,
+};
+
+struct bssmap_header {
+ uint8_t type;
+ uint8_t length;
+} __attribute__((packed));
+
+struct dtap_header {
+ uint8_t type;
+ uint8_t link_id;
+ uint8_t length;
+} __attribute__((packed));
+
+
+enum BSS_MAP_MSG_TYPE {
+ BSS_MAP_MSG_RESERVED_0 = 0,
+
+ /* ASSIGNMENT MESSAGES */
+ BSS_MAP_MSG_ASSIGMENT_RQST = 1,
+ BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
+ BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
+
+ /* HANDOVER MESSAGES */
+ BSS_MAP_MSG_HANDOVER_RQST = 16,
+ BSS_MAP_MSG_HANDOVER_REQUIRED = 17,
+ BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
+ BSS_MAP_MSG_HANDOVER_CMD = 19,
+ BSS_MAP_MSG_HANDOVER_COMPLETE = 20,
+ BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21,
+ BSS_MAP_MSG_HANDOVER_FAILURE = 22,
+ BSS_MAP_MSG_HANDOVER_PERFORMED = 23,
+ BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24,
+ BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
+ BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
+ BSS_MAP_MSG_HANDOVER_DETECT = 27,
+
+ /* RELEASE MESSAGES */
+ BSS_MAP_MSG_CLEAR_CMD = 32,
+ BSS_MAP_MSG_CLEAR_COMPLETE = 33,
+ BSS_MAP_MSG_CLEAR_RQST = 34,
+ BSS_MAP_MSG_RESERVED_1 = 35,
+ BSS_MAP_MSG_RESERVED_2 = 36,
+ BSS_MAP_MSG_SAPI_N_REJECT = 37,
+ BSS_MAP_MSG_CONFUSION = 38,
+
+ /* OTHER CONNECTION RELATED MESSAGES */
+ BSS_MAP_MSG_SUSPEND = 40,
+ BSS_MAP_MSG_RESUME = 41,
+ BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
+ BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43,
+ BSS_MAP_MSG_LSA_INFORMATION = 44,
+ BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
+ BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
+ BSS_MAP_MSG_COMMON_ID = 47,
+
+ /* GENERAL MESSAGES */
+ BSS_MAP_MSG_RESET = 48,
+ BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49,
+ BSS_MAP_MSG_OVERLOAD = 50,
+ BSS_MAP_MSG_RESERVED_3 = 51,
+ BSS_MAP_MSG_RESET_CIRCUIT = 52,
+ BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53,
+ BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
+ BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
+ BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
+
+ /* TERRESTRIAL RESOURCE MESSAGES */
+ BSS_MAP_MSG_BLOCK = 64,
+ BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65,
+ BSS_MAP_MSG_UNBLOCK = 66,
+ BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67,
+ BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68,
+ BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69,
+ BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70,
+ BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
+ BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72,
+ BSS_MAP_MSG_CHANGE_CIRCUIT = 78,
+ BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79,
+
+ /* RADIO RESOURCE MESSAGES */
+ BSS_MAP_MSG_RESOURCE_RQST = 80,
+ BSS_MAP_MSG_RESOURCE_INDICATION = 81,
+ BSS_MAP_MSG_PAGING = 82,
+ BSS_MAP_MSG_CIPHER_MODE_CMD = 83,
+ BSS_MAP_MSG_CLASSMARK_UPDATE = 84,
+ BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85,
+ BSS_MAP_MSG_QUEUING_INDICATION = 86,
+ BSS_MAP_MSG_COMPLETE_LAYER_3 = 87,
+ BSS_MAP_MSG_CLASSMARK_RQST = 88,
+ BSS_MAP_MSG_CIPHER_MODE_REJECT = 89,
+ BSS_MAP_MSG_LOAD_INDICATION = 90,
+
+ /* VGCS/VBS */
+ BSS_MAP_MSG_VGCS_VBS_SETUP = 4,
+ BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5,
+ BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29,
+ BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
+ BSS_MAP_MSG_UPLINK_RQST = 31,
+ BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
+ BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
+ BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
+ BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
+ BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
+ BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
+};
+
+enum GSM0808_IE_CODING {
+ GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1,
+ GSM0808_IE_RESERVED_0 = 2,
+ GSM0808_IE_RESOURCE_AVAILABLE = 3,
+ GSM0808_IE_CAUSE = 4,
+ GSM0808_IE_CELL_IDENTIFIER = 5,
+ GSM0808_IE_PRIORITY = 6,
+ GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7,
+ GSM0808_IE_IMSI = 8,
+ GSM0808_IE_TMSI = 9,
+ GSM0808_IE_ENCRYPTION_INFORMATION = 10,
+ GSM0808_IE_CHANNEL_TYPE = 11,
+ GSM0808_IE_PERIODICITY = 12,
+ GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13,
+ GSM0808_IE_NUMBER_OF_MSS = 14,
+ GSM0808_IE_RESERVED_1 = 15,
+ GSM0808_IE_RESERVED_2 = 16,
+ GSM0808_IE_RESERVED_3 = 17,
+ GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18,
+ GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19,
+ GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20,
+ GSM0808_IE_RR_CAUSE = 21,
+ GSM0808_IE_RESERVED_4 = 22,
+ GSM0808_IE_LAYER_3_INFORMATION = 23,
+ GSM0808_IE_DLCI = 24,
+ GSM0808_IE_DOWNLINK_DTX_FLAG = 25,
+ GSM0808_IE_CELL_IDENTIFIER_LIST = 26,
+ GSM0808_IE_RESPONSE_RQST = 27,
+ GSM0808_IE_RESOURCE_INDICATION_METHOD = 28,
+ GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29,
+ GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30,
+ GSM0808_IE_DIAGNOSTIC = 31,
+ GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32,
+ GSM0808_IE_CHOSEN_CHANNEL = 33,
+ GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34,
+ GSM0808_IE_CIPHER_RESPONSE_MODE = 35,
+ GSM0808_IE_CHANNEL_NEEDED = 36,
+ GSM0808_IE_TRACE_TYPE = 37,
+ GSM0808_IE_TRIGGERID = 38,
+ GSM0808_IE_TRACE_REFERENCE = 39,
+ GSM0808_IE_TRANSACTIONID = 40,
+ GSM0808_IE_MOBILE_IDENTITY = 41,
+ GSM0808_IE_OMCID = 42,
+ GSM0808_IE_FORWARD_INDICATOR = 43,
+ GSM0808_IE_CHOSEN_ENCR_ALG = 44,
+ GSM0808_IE_CIRCUIT_POOL = 45,
+ GSM0808_IE_CIRCUIT_POOL_LIST = 46,
+ GSM0808_IE_TIME_INDICATION = 47,
+ GSM0808_IE_RESOURCE_SITUATION = 48,
+ GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49,
+ GSM0808_IE_QUEUEING_INDICATOR = 50,
+ GSM0808_IE_SPEECH_VERSION = 64,
+ GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51,
+ GSM0808_IE_TALKER_FLAG = 53,
+ GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54,
+ GSM0808_IE_GROUP_CALL_REFERENCE = 55,
+ GSM0808_IE_EMLPP_PRIORITY = 56,
+ GSM0808_IE_CONFIG_EVO_INDI = 57,
+ GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58,
+ GSM0808_IE_LSA_IDENTIFIER = 59,
+ GSM0808_IE_LSA_IDENTIFIER_LIST = 60,
+ GSM0808_IE_LSA_INFORMATION = 61,
+ GSM0808_IE_LCS_QOS = 62,
+ GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63,
+ GSM0808_IE_LCS_PRIORITY = 67,
+ GSM0808_IE_LOCATION_TYPE = 68,
+ GSM0808_IE_LOCATION_ESTIMATE = 69,
+ GSM0808_IE_POSITIONING_DATA = 70,
+ GSM0808_IE_LCS_CAUSE = 71,
+ GSM0808_IE_LCS_CLIENT_TYPE = 72,
+ GSM0808_IE_APDU = 73,
+ GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74,
+ GSM0808_IE_GPS_ASSISTANCE_DATA = 75,
+ GSM0808_IE_DECIPHERING_KEYS = 76,
+ GSM0808_IE_RETURN_ERROR_RQST = 77,
+ GSM0808_IE_RETURN_ERROR_CAUSE = 78,
+ GSM0808_IE_SEGMENTATION = 79,
+ GSM0808_IE_SERVICE_HANDOVER = 80,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
+ GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
+ GSM0808_IE_RESERVED_5 = 65,
+ GSM0808_IE_RESERVED_6 = 66,
+};
+
+enum gsm0808_cause {
+ GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
+ GSM0808_CAUSE_UPLINK_QUALITY = 2,
+ GSM0808_CAUSE_UPLINK_STRENGTH = 3,
+ GSM0808_CAUSE_DOWNLINK_QUALITY = 4,
+ GSM0808_CAUSE_DOWNLINK_STRENGTH = 5,
+ GSM0808_CAUSE_DISTANCE = 6,
+ GSM0808_CAUSE_O_AND_M_INTERVENTION = 7,
+ GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8,
+ GSM0808_CAUSE_CALL_CONTROL = 9,
+ GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10,
+ GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11,
+ GSM0808_CAUSE_BETTER_CELL = 12,
+ GSM0808_CAUSE_DIRECTED_RETRY = 13,
+ GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
+ GSM0808_CAUSE_TRAFFIC = 15,
+ GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
+ GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
+ GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
+ GSM0808_CAUSE_CCCH_OVERLOAD = 35,
+ GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36,
+ GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37,
+ GSM0808_CAUSE_MS_NOT_EQUIPPED = 38,
+ GSM0808_CAUSE_INVALID_CELL = 39,
+ GSM0808_CAUSE_TRAFFIC_LOAD = 40,
+ GSM0808_CAUSE_PREEMPTION = 41,
+ GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
+ GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
+ GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
+ GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
+ GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
+ GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
+ GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
+ GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
+ GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
+ GSM0808_CAUSE_INCORRECT_VALUE = 83,
+ GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
+ GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
+ GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
+};
+
+/* GSM 08.08 3.2.2.11 Channel Type */
+enum gsm0808_chan_indicator {
+ GSM0808_CHAN_SPEECH = 1,
+ GSM0808_CHAN_DATA = 2,
+ GSM0808_CHAN_SIGN = 3,
+};
+
+enum gsm0808_chan_rate_type_data {
+ GSM0808_DATA_FULL_BM = 0x8,
+ GSM0808_DATA_HALF_LM = 0x9,
+ GSM0808_DATA_FULL_RPREF = 0xa,
+ GSM0808_DATA_HALF_PREF = 0xb,
+ GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
+ GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
+ GSM0808_DATA_MULTI_MASK = 0x20,
+ GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
+};
+
+enum gsm0808_chan_rate_type_speech {
+ GSM0808_SPEECH_FULL_BM = 0x8,
+ GSM0808_SPEECH_HALF_LM = 0x9,
+ GSM0808_SPEECH_FULL_PREF= 0xa,
+ GSM0808_SPEECH_HALF_PREF= 0xb,
+ GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a,
+ GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b,
+ GSM0808_SPEECH_PERM = 0xf,
+ GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
+};
+
+enum gsm0808_permitted_speech {
+ GSM0808_PERM_FR1 = 0x01,
+ GSM0808_PERM_FR2 = 0x11,
+ GSM0808_PERM_FR3 = 0x21,
+ GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4,
+ GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4,
+ GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
+};
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h
new file mode 100644
index 00000000..7dc35693
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h
@@ -0,0 +1,516 @@
+#ifndef PROTO_GSM_08_58_H
+#define PROTO_GSM_08_58_H
+
+/* GSM Radio Signalling Link messages on the A-bis interface
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (C) 2008 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>
+
+struct abis_rsl_common_hdr {
+ uint8_t msg_discr;
+ uint8_t msg_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 */
+struct abis_rsl_rll_hdr {
+ struct abis_rsl_common_hdr c;
+ uint8_t ie_chan;
+ uint8_t chan_nr;
+ uint8_t ie_link_id;
+ uint8_t link_id;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 and 8.4 */
+struct abis_rsl_dchan_hdr {
+ struct abis_rsl_common_hdr c;
+ uint8_t ie_chan;
+ uint8_t chan_nr;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+
+/* Chapter 9.1 */
+#define ABIS_RSL_MDISC_RLL 0x02
+#define ABIS_RSL_MDISC_DED_CHAN 0x08
+#define ABIS_RSL_MDISC_COM_CHAN 0x0c
+#define ABIS_RSL_MDISC_TRX 0x10
+#define ABIS_RSL_MDISC_LOC 0x20
+#define ABIS_RSL_MDISC_IPACCESS 0x7e
+#define ABIS_RSL_MDISC_TRANSP 0x01
+
+#define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01)
+
+/* Chapter 9.1 */
+enum abis_rsl_msgtype {
+ /* Radio Link Layer Management */
+ RSL_MT_DATA_REQ = 0x01,
+ RSL_MT_DATA_IND,
+ RSL_MT_ERROR_IND,
+ RSL_MT_EST_REQ,
+ RSL_MT_EST_CONF,
+ RSL_MT_EST_IND,
+ RSL_MT_REL_REQ,
+ RSL_MT_REL_CONF,
+ RSL_MT_REL_IND,
+ RSL_MT_UNIT_DATA_REQ,
+ RSL_MT_UNIT_DATA_IND, /* 0x0b */
+ RSL_MT_SUSP_REQ, /* non-standard elements */
+ RSL_MT_SUSP_CONF,
+ RSL_MT_RES_REQ,
+ RSL_MT_RECON_REQ, /* 0x0f */
+
+ /* Common Channel Management / TRX Management */
+ RSL_MT_BCCH_INFO = 0x11,
+ RSL_MT_CCCH_LOAD_IND,
+ RSL_MT_CHAN_RQD,
+ RSL_MT_DELETE_IND,
+ RSL_MT_PAGING_CMD,
+ RSL_MT_IMMEDIATE_ASSIGN_CMD,
+ RSL_MT_SMS_BC_REQ,
+ /* empty */
+ RSL_MT_RF_RES_IND = 0x19,
+ RSL_MT_SACCH_FILL,
+ RSL_MT_OVERLOAD,
+ RSL_MT_ERROR_REPORT,
+ RSL_MT_SMS_BC_CMD,
+ RSL_MT_CBCH_LOAD_IND,
+ RSL_MT_NOT_CMD, /* 0x1f */
+
+ /* Dedicate Channel Management */
+ RSL_MT_CHAN_ACTIV = 0x21,
+ RSL_MT_CHAN_ACTIV_ACK,
+ RSL_MT_CHAN_ACTIV_NACK,
+ RSL_MT_CONN_FAIL,
+ RSL_MT_DEACTIVATE_SACCH,
+ RSL_MT_ENCR_CMD,
+ RSL_MT_HANDO_DET,
+ RSL_MT_MEAS_RES,
+ RSL_MT_MODE_MODIFY_REQ,
+ RSL_MT_MODE_MODIFY_ACK,
+ RSL_MT_MODE_MODIFY_NACK,
+ RSL_MT_PHY_CONTEXT_REQ,
+ RSL_MT_PHY_CONTEXT_CONF,
+ RSL_MT_RF_CHAN_REL,
+ RSL_MT_MS_POWER_CONTROL,
+ RSL_MT_BS_POWER_CONTROL, /* 0x30 */
+ RSL_MT_PREPROC_CONFIG,
+ RSL_MT_PREPROC_MEAS_RES,
+ RSL_MT_RF_CHAN_REL_ACK,
+ RSL_MT_SACCH_INFO_MODIFY,
+ RSL_MT_TALKER_DET,
+ RSL_MT_LISTENER_DET,
+ RSL_MT_REMOTE_CODEC_CONF_REP,
+ RSL_MT_RTD_REP,
+ RSL_MT_PRE_HANDO_NOTIF,
+ RSL_MT_MR_CODEC_MOD_REQ,
+ RSL_MT_MR_CODEC_MOD_ACK,
+ RSL_MT_MR_CODEC_MOD_NACK,
+ RSL_MT_MR_CODEC_MOD_PER,
+ RSL_MT_TFO_REP,
+ RSL_MT_TFO_MOD_REQ, /* 0x3f */
+ RSL_MT_LOCATION_INFO = 0x41,
+
+ /* ip.access specific RSL message types */
+ RSL_MT_IPAC_DIR_RETR_ENQ = 0x40,
+ RSL_MT_IPAC_PDCH_ACT = 0x48,
+ RSL_MT_IPAC_PDCH_ACT_ACK,
+ RSL_MT_IPAC_PDCH_ACT_NACK,
+ RSL_MT_IPAC_PDCH_DEACT = 0x4b,
+ RSL_MT_IPAC_PDCH_DEACT_ACK,
+ RSL_MT_IPAC_PDCH_DEACT_NACK,
+ RSL_MT_IPAC_CONNECT_MUX = 0x50,
+ RSL_MT_IPAC_CONNECT_MUX_ACK,
+ RSL_MT_IPAC_CONNECT_MUX_NACK,
+ RSL_MT_IPAC_BIND_MUX = 0x53,
+ RSL_MT_IPAC_BIND_MUX_ACK,
+ RSL_MT_IPAC_BIND_MUX_NACK,
+ RSL_MT_IPAC_DISC_MUX = 0x56,
+ RSL_MT_IPAC_DISC_MUX_ACK,
+ RSL_MT_IPAC_DISC_MUX_NACK,
+ RSL_MT_IPAC_CRCX = 0x70, /* Bind to local BTS RTP port */
+ RSL_MT_IPAC_CRCX_ACK,
+ RSL_MT_IPAC_CRCX_NACK,
+ RSL_MT_IPAC_MDCX = 0x73,
+ RSL_MT_IPAC_MDCX_ACK,
+ RSL_MT_IPAC_MDCX_NACK,
+ RSL_MT_IPAC_DLCX_IND = 0x76,
+ RSL_MT_IPAC_DLCX = 0x77,
+ RSL_MT_IPAC_DLCX_ACK,
+ RSL_MT_IPAC_DLCX_NACK,
+};
+
+/* Siemens vendor-specific */
+enum abis_rsl_msgtype_siemens {
+ RSL_MT_SIEMENS_MRPCI = 0x41,
+ RSL_MT_SIEMENS_INTRAC_HO_COND_IND = 0x42,
+ RSL_MT_SIEMENS_INTERC_HO_COND_IND = 0x43,
+ RSL_MT_SIEMENS_FORCED_HO_REQ = 0x44,
+ RSL_MT_SIEMENS_PREF_AREA_REQ = 0x45,
+ RSL_MT_SIEMENS_PREF_AREA = 0x46,
+ RSL_MT_SIEMENS_START_TRACE = 0x47,
+ RSL_MT_SIEMENS_START_TRACE_ACK = 0x48,
+ RSL_MT_SIEMENS_STOP_TRACE = 0x49,
+ RSL_MT_SIEMENS_TRMR = 0x4a,
+ RSL_MT_SIEMENS_HO_FAIL_IND = 0x4b,
+ RSL_MT_SIEMENS_STOP_TRACE_ACK = 0x4c,
+ RSL_MT_SIEMENS_UPLF = 0x4d,
+ RSL_MT_SIEMENS_UPLB = 0x4e,
+ RSL_MT_SIEMENS_SET_SYS_INFO_10 = 0x4f,
+ RSL_MT_SIEMENS_MODIF_COND_IND = 0x50,
+};
+
+/* Chapter 9.3 */
+enum abis_rsl_ie {
+ RSL_IE_CHAN_NR = 0x01,
+ RSL_IE_LINK_IDENT,
+ RSL_IE_ACT_TYPE,
+ RSL_IE_BS_POWER,
+ RSL_IE_CHAN_IDENT,
+ RSL_IE_CHAN_MODE,
+ RSL_IE_ENCR_INFO,
+ RSL_IE_FRAME_NUMBER,
+ RSL_IE_HANDO_REF,
+ RSL_IE_L1_INFO,
+ RSL_IE_L3_INFO,
+ RSL_IE_MS_IDENTITY,
+ RSL_IE_MS_POWER,
+ RSL_IE_PAGING_GROUP,
+ RSL_IE_PAGING_LOAD,
+ RSL_IE_PYHS_CONTEXT = 0x10,
+ RSL_IE_ACCESS_DELAY,
+ RSL_IE_RACH_LOAD,
+ RSL_IE_REQ_REFERENCE,
+ RSL_IE_RELEASE_MODE,
+ RSL_IE_RESOURCE_INFO,
+ RSL_IE_RLM_CAUSE,
+ RSL_IE_STARTNG_TIME,
+ RSL_IE_TIMING_ADVANCE,
+ RSL_IE_UPLINK_MEAS,
+ RSL_IE_CAUSE,
+ RSL_IE_MEAS_RES_NR,
+ RSL_IE_MSG_ID,
+ /* reserved */
+ RSL_IE_SYSINFO_TYPE = 0x1e,
+ RSL_IE_MS_POWER_PARAM,
+ RSL_IE_BS_POWER_PARAM,
+ RSL_IE_PREPROC_PARAM,
+ RSL_IE_PREPROC_MEAS,
+ RSL_IE_IMM_ASS_INFO, /* Phase 1 (3.6.0), later Full below */
+ RSL_IE_SMSCB_INFO = 0x24,
+ RSL_IE_MS_TIMING_OFFSET,
+ RSL_IE_ERR_MSG,
+ RSL_IE_FULL_BCCH_INFO,
+ RSL_IE_CHAN_NEEDED,
+ RSL_IE_CB_CMD_TYPE,
+ RSL_IE_SMSCB_MSG,
+ RSL_IE_FULL_IMM_ASS_INFO,
+ RSL_IE_SACCH_INFO,
+ RSL_IE_CBCH_LOAD_INFO,
+ RSL_IE_SMSCB_CHAN_INDICATOR,
+ RSL_IE_GROUP_CALL_REF,
+ RSL_IE_CHAN_DESC = 0x30,
+ RSL_IE_NCH_DRX_INFO,
+ RSL_IE_CMD_INDICATOR,
+ RSL_IE_EMLPP_PRIO,
+ RSL_IE_UIC,
+ RSL_IE_MAIN_CHAN_REF,
+ RSL_IE_MR_CONFIG,
+ RSL_IE_MR_CONTROL,
+ RSL_IE_SUP_CODEC_TYPES,
+ RSL_IE_CODEC_CONFIG,
+ RSL_IE_RTD,
+ RSL_IE_TFO_STATUS,
+ RSL_IE_LLP_APDU,
+ /* Siemens vendor-specific */
+ RSL_IE_SIEMENS_MRPCI = 0x40,
+ RSL_IE_SIEMENS_PREF_AREA_TYPE = 0x43,
+ RSL_IE_SIEMENS_ININ_CELL_HO_PAR = 0x45,
+ RSL_IE_SIEMENS_TRACE_REF_NR = 0x46,
+ RSL_IE_SIEMENS_INT_TRACE_IDX = 0x47,
+ RSL_IE_SIEMENS_L2_HDR_INFO = 0x48,
+ RSL_IE_SIEMENS_HIGHEST_RATE = 0x4e,
+ RSL_IE_SIEMENS_SUGGESTED_RATE = 0x4f,
+
+ /* ip.access */
+ RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
+ RSL_IE_IPAC_PROXY_UDP = 0xe1,
+ RSL_IE_IPAC_BSCMPL_TOUT = 0xe2,
+ RSL_IE_IPAC_REMOTE_IP = 0xf0,
+ RSL_IE_IPAC_REMOTE_PORT = 0xf1,
+ RSL_IE_IPAC_RTP_PAYLOAD = 0xf2,
+ RSL_IE_IPAC_LOCAL_PORT = 0xf3,
+ RSL_IE_IPAC_SPEECH_MODE = 0xf4,
+ RSL_IE_IPAC_LOCAL_IP = 0xf5,
+ RSL_IE_IPAC_CONN_STAT = 0xf6,
+ RSL_IE_IPAC_HO_C_PARMS = 0xf7,
+ RSL_IE_IPAC_CONN_ID = 0xf8,
+ RSL_IE_IPAC_RTP_CSD_FMT = 0xf9,
+ RSL_IE_IPAC_RTP_JIT_BUF = 0xfa,
+ RSL_IE_IPAC_RTP_COMPR = 0xfb,
+ RSL_IE_IPAC_RTP_PAYLOAD2= 0xfc,
+ RSL_IE_IPAC_RTP_MPLEX = 0xfd,
+ RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe,
+};
+
+/* Chapter 9.3.1 */
+#define RSL_CHAN_NR_MASK 0xf8
+#define RSL_CHAN_Bm_ACCHs 0x08
+#define RSL_CHAN_Lm_ACCHs 0x10 /* .. 0x18 */
+#define RSL_CHAN_SDCCH4_ACCH 0x20 /* .. 0x38 */
+#define RSL_CHAN_SDCCH8_ACCH 0x40 /* ...0x78 */
+#define RSL_CHAN_BCCH 0x80
+#define RSL_CHAN_RACH 0x88
+#define RSL_CHAN_PCH_AGCH 0x90
+
+/* Chapter 9.3.3 */
+#define RSL_ACT_TYPE_INITIAL 0x00
+#define RSL_ACT_TYPE_REACT 0x80
+#define RSL_ACT_INTRA_IMM_ASS 0x00
+#define RSL_ACT_INTRA_NORM_ASS 0x01
+#define RSL_ACT_INTER_ASYNC 0x02
+#define RSL_ACT_INTER_SYNC 0x03
+#define RSL_ACT_SECOND_ADD 0x04
+#define RSL_ACT_SECOND_MULTI 0x05
+
+/* Chapter 9.3.6 */
+struct rsl_ie_chan_mode {
+ uint8_t dtx_dtu;
+ uint8_t spd_ind;
+ uint8_t chan_rt;
+ uint8_t chan_rate;
+} __attribute__ ((packed));
+#define RSL_CMOD_DTXu 0x01 /* uplink */
+#define RSL_CMOD_DTXd 0x02 /* downlink */
+enum rsl_cmod_spd {
+ RSL_CMOD_SPD_SPEECH = 0x01,
+ RSL_CMOD_SPD_DATA = 0x02,
+ RSL_CMOD_SPD_SIGN = 0x03,
+};
+#define RSL_CMOD_CRT_SDCCH 0x01
+#define RSL_CMOD_CRT_TCH_Bm 0x08 /* full-rate */
+#define RSL_CMOD_CRT_TCH_Lm 0x09 /* half-rate */
+/* FIXME: More CRT types */
+/* Speech */
+#define RSL_CMOD_SP_GSM1 0x01
+#define RSL_CMOD_SP_GSM2 0x11
+#define RSL_CMOD_SP_GSM3 0x21
+/* Data */
+#define RSL_CMOD_SP_NT_14k5 0x58
+#define RSL_CMOD_SP_NT_12k0 0x50
+#define RSL_CMOD_SP_NT_6k0 0x51
+
+/* Chapter 9.3.5 */
+struct rsl_ie_chan_ident {
+ /* GSM 04.08 10.5.2.5 */
+ struct {
+ uint8_t iei;
+ uint8_t chan_nr; /* enc_chan_nr */
+ uint8_t oct3;
+ uint8_t oct4;
+ } chan_desc;
+#if 0 /* spec says we need this but Abissim doesn't use it */
+ struct {
+ uint8_t tag;
+ uint8_t len;
+ } mobile_alloc;
+#endif
+} __attribute__ ((packed));
+
+/* Chapter 9.3.22 */
+#define RLL_CAUSE_T200_EXPIRED 0x01
+#define RLL_CAUSE_REEST_REQ 0x02
+#define RLL_CAUSE_UNSOL_UA_RESP 0x03
+#define RLL_CAUSE_UNSOL_DM_RESP 0x04
+#define RLL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define RLL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define RLL_CAUSE_SEQ_ERR 0x07
+#define RLL_CAUSE_UFRM_INC_PARAM 0x08
+#define RLL_CAUSE_SFRM_INC_PARAM 0x09
+#define RLL_CAUSE_IFRM_INC_MBITS 0x0a
+#define RLL_CAUSE_IFRM_INC_LEN 0x0b
+#define RLL_CAUSE_FRM_UNIMPL 0x0c
+#define RLL_CAUSE_SABM_MF 0x0d
+#define RLL_CAUSE_SABM_INFO_NOTALL 0x0e
+
+/* Chapter 9.3.26 */
+#define RSL_ERRCLS_NORMAL 0x00
+#define RSL_ERRCLS_RESOURCE_UNAVAIL 0x20
+#define RSL_ERRCLS_SERVICE_UNAVAIL 0x30
+#define RSL_ERRCLS_SERVICE_UNIMPL 0x40
+#define RSL_ERRCLS_INVAL_MSG 0x50
+#define RSL_ERRCLS_PROTO_ERROR 0x60
+#define RSL_ERRCLS_INTERWORKING 0x70
+
+/* normal event */
+#define RSL_ERR_RADIO_IF_FAIL 0x00
+#define RSL_ERR_RADIO_LINK_FAIL 0x01
+#define RSL_ERR_HANDOVER_ACC_FAIL 0x02
+#define RSL_ERR_TALKER_ACC_FAIL 0x03
+#define RSL_ERR_OM_INTERVENTION 0x07
+#define RSL_ERR_NORMAL_UNSPEC 0x0f
+#define RSL_ERR_T_MSRFPCI_EXP 0x18
+/* resource unavailable */
+#define RSL_ERR_EQUIPMENT_FAIL 0x20
+#define RSL_ERR_RR_UNAVAIL 0x21
+#define RSL_ERR_TERR_CH_FAIL 0x22
+#define RSL_ERR_CCCH_OVERLOAD 0x23
+#define RSL_ERR_ACCH_OVERLOAD 0x24
+#define RSL_ERR_PROCESSOR_OVERLOAD 0x25
+#define RSL_ERR_RES_UNAVAIL 0x2f
+/* service or option not available */
+#define RSL_ERR_TRANSC_UNAVAIL 0x30
+#define RSL_ERR_SERV_OPT_UNAVAIL 0x3f
+/* service or option not implemented */
+#define RSL_ERR_ENCR_UNIMPL 0x40
+#define RSL_ERR_SERV_OPT_UNIMPL 0x4f
+/* invalid message */
+#define RSL_ERR_RCH_ALR_ACTV_ALLOC 0x50
+#define RSL_ERR_INVALID_MESSAGE 0x5f
+/* protocol error */
+#define RSL_ERR_MSG_DISCR 0x60
+#define RSL_ERR_MSG_TYPE 0x61
+#define RSL_ERR_MSG_SEQ 0x62
+#define RSL_ERR_IE_ERROR 0x63
+#define RSL_ERR_MAND_IE_ERROR 0x64
+#define RSL_ERR_OPT_IE_ERROR 0x65
+#define RSL_ERR_IE_NONEXIST 0x66
+#define RSL_ERR_IE_LENGTH 0x67
+#define RSL_ERR_IE_CONTENT 0x68
+#define RSL_ERR_PROTO 0x6f
+/* interworking */
+#define RSL_ERR_INTERWORKING 0x7f
+
+/* Chapter 9.3.30 */
+#define RSL_SYSTEM_INFO_8 0x00
+#define RSL_SYSTEM_INFO_1 0x01
+#define RSL_SYSTEM_INFO_2 0x02
+#define RSL_SYSTEM_INFO_3 0x03
+#define RSL_SYSTEM_INFO_4 0x04
+#define RSL_SYSTEM_INFO_5 0x05
+#define RSL_SYSTEM_INFO_6 0x06
+#define RSL_SYSTEM_INFO_7 0x07
+#define RSL_SYSTEM_INFO_16 0x08
+#define RSL_SYSTEM_INFO_17 0x09
+#define RSL_SYSTEM_INFO_2bis 0x0a
+#define RSL_SYSTEM_INFO_2ter 0x0b
+#define RSL_SYSTEM_INFO_5bis 0x0d
+#define RSL_SYSTEM_INFO_5ter 0x0e
+#define RSL_SYSTEM_INFO_10 0x0f
+#define REL_EXT_MEAS_ORDER 0x47
+#define RSL_MEAS_INFO 0x48
+#define RSL_SYSTEM_INFO_13 0x28
+#define RSL_SYSTEM_INFO_2quater 0x29
+#define RSL_SYSTEM_INFO_9 0x2a
+#define RSL_SYSTEM_INFO_18 0x2b
+#define RSL_SYSTEM_INFO_19 0x2c
+#define RSL_SYSTEM_INFO_20 0x2d
+
+/* Chapter 9.3.40 */
+#define RSL_CHANNEED_ANY 0x00
+#define RSL_CHANNEED_SDCCH 0x01
+#define RSL_CHANNEED_TCH_F 0x02
+#define RSL_CHANNEED_TCH_ForH 0x03
+
+/* Chapter 3.3.2.3 Brocast control channel */
+/* CCCH-CONF, NC is not combined */
+#define RSL_BCCH_CCCH_CONF_1_NC 0x00
+#define RSL_BCCH_CCCH_CONF_1_C 0x01
+#define RSL_BCCH_CCCH_CONF_2_NC 0x02
+#define RSL_BCCH_CCCH_CONF_3_NC 0x04
+#define RSL_BCCH_CCCH_CONF_4_NC 0x06
+
+/* BS-PA-MFRMS */
+#define RSL_BS_PA_MFRMS_2 0x00
+#define RSL_BS_PA_MFRMS_3 0x01
+#define RSL_BS_PA_MFRMS_4 0x02
+#define RSL_BS_PA_MFRMS_5 0x03
+#define RSL_BS_PA_MFRMS_6 0x04
+#define RSL_BS_PA_MFRMS_7 0x05
+#define RSL_BS_PA_MFRMS_8 0x06
+#define RSL_BS_PA_MFRMS_9 0x07
+
+/* RSL_IE_IPAC_RTP_PAYLOAD[2] */
+enum rsl_ipac_rtp_payload {
+ RSL_IPAC_RTP_GSM = 1,
+ RSL_IPAC_RTP_EFR,
+ RSL_IPAC_RTP_AMR,
+ RSL_IPAC_RTP_CSD,
+ RSL_IPAC_RTP_MUX,
+};
+
+/* RSL_IE_IPAC_SPEECH_MODE, lower four bits */
+enum rsl_ipac_speech_mode_s {
+ RSL_IPAC_SPEECH_GSM_FR = 0, /* GSM FR (Type 1, FS) */
+ RSL_IPAC_SPEECH_GSM_EFR = 1, /* GSM EFR (Type 2, FS) */
+ RSL_IPAC_SPEECH_GSM_AMR_FR = 2, /* GSM AMR/FR (Type 3, FS) */
+ RSL_IPAC_SPEECH_GSM_HR = 3, /* GSM HR (Type 1, HS) */
+ RSL_IPAC_SPEECH_GSM_AMR_HR = 5, /* GSM AMR/hr (Type 3, HS) */
+ RSL_IPAC_SPEECH_AS_RTP = 0xf, /* As specified by RTP Payload IE */
+};
+/* RSL_IE_IPAC_SPEECH_MODE, upper four bits */
+enum rsl_ipac_speech_mode_m {
+ RSL_IPAC_SPEECH_M_RXTX = 0, /* Send and Receive */
+ RSL_IPAC_SPEECH_M_RX = 1, /* Receive only */
+ RSL_IPAC_SPEECH_M_TX = 2, /* Send only */
+};
+
+/* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */
+enum rsl_ipac_rtp_csd_format_d {
+ RSL_IPAC_RTP_CSD_EXT_TRAU = 0,
+ RSL_IPAC_RTP_CSD_NON_TRAU = 1,
+ RSL_IPAC_RTP_CSD_TRAU_BTS = 2,
+ RSL_IPAC_RTP_CSD_IWF_FREE = 3,
+};
+/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */
+enum rsl_ipac_rtp_csd_format_ir {
+ RSL_IPAC_RTP_CSD_IR_8k = 0,
+ RSL_IPAC_RTP_CSD_IR_16k = 1,
+ RSL_IPAC_RTP_CSD_IR_32k = 2,
+ RSL_IPAC_RTP_CSD_IR_64k = 3,
+};
+
+/* Siemens vendor-specific RSL extensions */
+struct rsl_mrpci {
+ uint8_t power_class:3,
+ vgcs_capable:1,
+ vbs_capable:1,
+ gsm_phase:2;
+} __attribute__ ((packed));
+
+enum rsl_mrpci_pwrclass {
+ RSL_MRPCI_PWRC_1 = 0,
+ RSL_MRPCI_PWRC_2 = 1,
+ RSL_MRPCI_PWRC_3 = 2,
+ RSL_MRPCI_PWRC_4 = 3,
+ RSL_MRPCI_PWRC_5 = 4,
+};
+enum rsl_mrpci_phase {
+ RSL_MRPCI_PHASE_1 = 0,
+ /* reserved */
+ RSL_MRPCI_PHASE_2 = 2,
+ RSL_MRPCI_PHASE_2PLUS = 3,
+};
+
+
+#endif /* PROTO_GSM_08_58_H */
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h
new file mode 100644
index 00000000..9cae45da
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h
@@ -0,0 +1,713 @@
+#ifndef PROTO_GSM_12_21_H
+#define PROTO_GSM_12_21_H
+
+/* GSM Network Management messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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 <osmocore/tlv.h>
+
+/* generic header in front of every OML message according to TS 08.59 */
+struct abis_om_hdr {
+ uint8_t mdisc;
+ uint8_t placement;
+ uint8_t sequence;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_MDISC_FOM 0x80
+#define ABIS_OM_MDISC_MMI 0x40
+#define ABIS_OM_MDISC_TRAU 0x20
+#define ABIS_OM_MDISC_MANUF 0x10
+#define ABIS_OM_PLACEMENT_ONLY 0x80
+#define ABIS_OM_PLACEMENT_FIRST 0x40
+#define ABIS_OM_PLACEMENT_MIDDLE 0x20
+#define ABIS_OM_PLACEMENT_LAST 0x10
+
+struct abis_om_obj_inst {
+ uint8_t bts_nr;
+ uint8_t trx_nr;
+ uint8_t ts_nr;
+} __attribute__ ((packed));
+
+struct abis_om_fom_hdr {
+ uint8_t msg_type;
+ uint8_t obj_class;
+ struct abis_om_obj_inst obj_inst;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr))
+
+/* Section 9.1: Message Types */
+enum abis_nm_msgtype {
+ /* SW Download Management Messages */
+ NM_MT_LOAD_INIT = 0x01,
+ NM_MT_LOAD_INIT_ACK,
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_SEG,
+ NM_MT_LOAD_SEG_ACK,
+ NM_MT_LOAD_ABORT,
+ NM_MT_LOAD_END,
+ NM_MT_LOAD_END_ACK,
+ NM_MT_LOAD_END_NACK,
+ NM_MT_SW_ACT_REQ, /* BTS->BSC */
+ NM_MT_SW_ACT_REQ_ACK,
+ NM_MT_SW_ACT_REQ_NACK,
+ NM_MT_ACTIVATE_SW, /* BSC->BTS */
+ NM_MT_ACTIVATE_SW_ACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_SW_ACTIVATED_REP, /* 0x10 */
+ /* A-bis Interface Management Messages */
+ NM_MT_ESTABLISH_TEI = 0x21,
+ NM_MT_ESTABLISH_TEI_ACK,
+ NM_MT_ESTABLISH_TEI_NACK,
+ NM_MT_CONN_TERR_SIGN,
+ NM_MT_CONN_TERR_SIGN_ACK,
+ NM_MT_CONN_TERR_SIGN_NACK,
+ NM_MT_DISC_TERR_SIGN,
+ NM_MT_DISC_TERR_SIGN_ACK,
+ NM_MT_DISC_TERR_SIGN_NACK,
+ NM_MT_CONN_TERR_TRAF,
+ NM_MT_CONN_TERR_TRAF_ACK,
+ NM_MT_CONN_TERR_TRAF_NACK,
+ NM_MT_DISC_TERR_TRAF,
+ NM_MT_DISC_TERR_TRAF_ACK,
+ NM_MT_DISC_TERR_TRAF_NACK,
+ /* Transmission Management Messages */
+ NM_MT_CONN_MDROP_LINK = 0x31,
+ NM_MT_CONN_MDROP_LINK_ACK,
+ NM_MT_CONN_MDROP_LINK_NACK,
+ NM_MT_DISC_MDROP_LINK,
+ NM_MT_DISC_MDROP_LINK_ACK,
+ NM_MT_DISC_MDROP_LINK_NACK,
+ /* Air Interface Management Messages */
+ NM_MT_SET_BTS_ATTR = 0x41,
+ NM_MT_SET_BTS_ATTR_ACK,
+ NM_MT_SET_BTS_ATTR_NACK,
+ NM_MT_SET_RADIO_ATTR,
+ NM_MT_SET_RADIO_ATTR_ACK,
+ NM_MT_SET_RADIO_ATTR_NACK,
+ NM_MT_SET_CHAN_ATTR,
+ NM_MT_SET_CHAN_ATTR_ACK,
+ NM_MT_SET_CHAN_ATTR_NACK,
+ /* Test Management Messages */
+ NM_MT_PERF_TEST = 0x51,
+ NM_MT_PERF_TEST_ACK,
+ NM_MT_PERF_TEST_NACK,
+ NM_MT_TEST_REP,
+ NM_MT_SEND_TEST_REP,
+ NM_MT_SEND_TEST_REP_ACK,
+ NM_MT_SEND_TEST_REP_NACK,
+ NM_MT_STOP_TEST,
+ NM_MT_STOP_TEST_ACK,
+ NM_MT_STOP_TEST_NACK,
+ /* State Management and Event Report Messages */
+ NM_MT_STATECHG_EVENT_REP = 0x61,
+ NM_MT_FAILURE_EVENT_REP,
+ NM_MT_STOP_EVENT_REP,
+ NM_MT_STOP_EVENT_REP_ACK,
+ NM_MT_STOP_EVENT_REP_NACK,
+ NM_MT_REST_EVENT_REP,
+ NM_MT_REST_EVENT_REP_ACK,
+ NM_MT_REST_EVENT_REP_NACK,
+ NM_MT_CHG_ADM_STATE,
+ NM_MT_CHG_ADM_STATE_ACK,
+ NM_MT_CHG_ADM_STATE_NACK,
+ NM_MT_CHG_ADM_STATE_REQ,
+ NM_MT_CHG_ADM_STATE_REQ_ACK,
+ NM_MT_CHG_ADM_STATE_REQ_NACK,
+ NM_MT_REP_OUTST_ALARMS = 0x93,
+ NM_MT_REP_OUTST_ALARMS_ACK,
+ NM_MT_REP_OUTST_ALARMS_NACK,
+ /* Equipment Management Messages */
+ NM_MT_CHANGEOVER = 0x71,
+ NM_MT_CHANGEOVER_ACK,
+ NM_MT_CHANGEOVER_NACK,
+ NM_MT_OPSTART,
+ NM_MT_OPSTART_ACK,
+ NM_MT_OPSTART_NACK,
+ NM_MT_REINIT,
+ NM_MT_REINIT_ACK,
+ NM_MT_REINIT_NACK,
+ NM_MT_SET_SITE_OUT, /* BS11: get alarm ?!? */
+ NM_MT_SET_SITE_OUT_ACK,
+ NM_MT_SET_SITE_OUT_NACK,
+ NM_MT_CHG_HW_CONF = 0x90,
+ NM_MT_CHG_HW_CONF_ACK,
+ NM_MT_CHG_HW_CONF_NACK,
+ /* Measurement Management Messages */
+ NM_MT_MEAS_RES_REQ = 0x8a,
+ NM_MT_MEAS_RES_RESP,
+ NM_MT_STOP_MEAS,
+ NM_MT_START_MEAS,
+ /* Other Messages */
+ NM_MT_GET_ATTR = 0x81,
+ NM_MT_GET_ATTR_RESP,
+ NM_MT_GET_ATTR_NACK,
+ NM_MT_SET_ALARM_THRES,
+ NM_MT_SET_ALARM_THRES_ACK,
+ NM_MT_SET_ALARM_THRES_NACK,
+};
+
+enum abis_nm_msgtype_bs11 {
+ NM_MT_BS11_RESET_RESOURCE = 0x74,
+
+ NM_MT_BS11_BEGIN_DB_TX = 0xa3,
+ NM_MT_BS11_BEGIN_DB_TX_ACK,
+ NM_MT_BS11_BEGIN_DB_TX_NACK,
+ NM_MT_BS11_END_DB_TX = 0xa6,
+ NM_MT_BS11_END_DB_TX_ACK,
+ NM_MT_BS11_END_DB_TX_NACK,
+ NM_MT_BS11_CREATE_OBJ = 0xa9,
+ NM_MT_BS11_CREATE_OBJ_ACK,
+ NM_MT_BS11_CREATE_OBJ_NACK,
+ NM_MT_BS11_DELETE_OBJ = 0xac,
+ NM_MT_BS11_DELETE_OBJ_ACK,
+ NM_MT_BS11_DELETE_OBJ_NACK,
+
+ NM_MT_BS11_SET_ATTR = 0xd0,
+ NM_MT_BS11_SET_ATTR_ACK,
+ NM_MT_BS11_SET_ATTR_NACK,
+ NM_MT_BS11_LMT_SESSION = 0xdc,
+
+ NM_MT_BS11_GET_STATE = 0xe3,
+ NM_MT_BS11_GET_STATE_ACK,
+ NM_MT_BS11_LMT_LOGON = 0xe5,
+ NM_MT_BS11_LMT_LOGON_ACK,
+ NM_MT_BS11_RESTART = 0xe7,
+ NM_MT_BS11_RESTART_ACK,
+ NM_MT_BS11_DISCONNECT = 0xe9,
+ NM_MT_BS11_DISCONNECT_ACK,
+ NM_MT_BS11_LMT_LOGOFF = 0xec,
+ NM_MT_BS11_LMT_LOGOFF_ACK,
+ NM_MT_BS11_RECONNECT = 0xf1,
+ NM_MT_BS11_RECONNECT_ACK,
+};
+
+enum abis_nm_msgtype_ipacc {
+ NM_MT_IPACC_RESTART = 0x87,
+ NM_MT_IPACC_RESTART_ACK,
+ NM_MT_IPACC_RESTART_NACK,
+ NM_MT_IPACC_RSL_CONNECT = 0xe0,
+ NM_MT_IPACC_RSL_CONNECT_ACK,
+ NM_MT_IPACC_RSL_CONNECT_NACK,
+ NM_MT_IPACC_RSL_DISCONNECT = 0xe3,
+ NM_MT_IPACC_RSL_DISCONNECT_ACK,
+ NM_MT_IPACC_RSL_DISCONNECT_NACK,
+ NM_MT_IPACC_CONN_TRAF = 0xe6,
+ NM_MT_IPACC_CONN_TRAF_ACK,
+ NM_MT_IPACC_CONN_TRAF_NACK,
+ NM_MT_IPACC_DEF_BOOT_SW = 0xec,
+ NM_MT_IPACC_DEF_BOOT_SW_ACK,
+ MN_MT_IPACC_DEF_BOOT_SW_NACK,
+ NM_MT_IPACC_SET_NVATTR = 0xef,
+ NM_MT_IPACC_SET_NVATTR_ACK,
+ NM_MT_IPACC_SET_NVATTR_NACK,
+ NM_MT_IPACC_GET_NVATTR = 0xf2,
+ NM_MT_IPACC_GET_NVATTR_ACK,
+ NM_MT_IPACC_GET_NVATTR_NACK,
+ NM_MT_IPACC_SET_ATTR = 0xf5,
+ NM_MT_IPACC_SET_ATTR_ACK,
+ NM_MT_IPACC_SET_ATTR_NACK,
+};
+
+enum abis_nm_bs11_cell_alloc {
+ NM_BS11_CANR_GSM = 0x00,
+ NM_BS11_CANR_DCS1800 = 0x01,
+};
+
+/* Section 9.2: Object Class */
+enum abis_nm_obj_class {
+ NM_OC_SITE_MANAGER = 0x00,
+ NM_OC_BTS,
+ NM_OC_RADIO_CARRIER,
+ NM_OC_CHANNEL,
+ NM_OC_BASEB_TRANSC,
+ /* RFU: 05-FE */
+
+ NM_OC_IPAC_E1_TRUNK = 0x0e,
+ NM_OC_IPAC_E1_PORT = 0x0f,
+ NM_OC_IPAC_E1_CHAN = 0x10,
+ NM_OC_IPAC_CLK_MODULE = 0x22,
+
+ NM_OC_BS11_ADJC = 0xa0,
+ NM_OC_BS11_HANDOVER = 0xa1,
+ NM_OC_BS11_PWR_CTRL = 0xa2,
+ NM_OC_BS11_BTSE = 0xa3, /* LMT? */
+ NM_OC_BS11_RACK = 0xa4,
+ NM_OC_BS11 = 0xa5, /* 01: ALCO */
+ NM_OC_BS11_TEST = 0xa6,
+ NM_OC_BS11_ENVABTSE = 0xa8,
+ NM_OC_BS11_BPORT = 0xa9,
+
+ NM_OC_GPRS_NSE = 0xf0,
+ NM_OC_GPRS_CELL = 0xf1,
+ NM_OC_GPRS_NSVC = 0xf2,
+
+ NM_OC_NULL = 0xff,
+};
+
+/* Section 9.4: Attributes */
+enum abis_nm_attr {
+ NM_ATT_ABIS_CHANNEL = 0x01,
+ NM_ATT_ADD_INFO,
+ NM_ATT_ADD_TEXT,
+ NM_ATT_ADM_STATE,
+ NM_ATT_ARFCN_LIST,
+ NM_ATT_AUTON_REPORT,
+ NM_ATT_AVAIL_STATUS,
+ NM_ATT_BCCH_ARFCN,
+ NM_ATT_BSIC,
+ NM_ATT_BTS_AIR_TIMER,
+ NM_ATT_CCCH_L_I_P,
+ NM_ATT_CCCH_L_T,
+ NM_ATT_CHAN_COMB,
+ NM_ATT_CONN_FAIL_CRIT,
+ NM_ATT_DEST,
+ /* res */
+ NM_ATT_EVENT_TYPE = 0x11, /* BS11: file data ?!? */
+ NM_ATT_FILE_ID,
+ NM_ATT_FILE_VERSION,
+ NM_ATT_GSM_TIME,
+ NM_ATT_HSN,
+ NM_ATT_HW_CONFIG,
+ NM_ATT_HW_DESC,
+ NM_ATT_INTAVE_PARAM,
+ NM_ATT_INTERF_BOUND,
+ NM_ATT_LIST_REQ_ATTR,
+ NM_ATT_MAIO,
+ NM_ATT_MANUF_STATE,
+ NM_ATT_MANUF_THRESH,
+ NM_ATT_MANUF_ID,
+ NM_ATT_MAX_TA,
+ NM_ATT_MDROP_LINK, /* 0x20 */
+ NM_ATT_MDROP_NEXT,
+ NM_ATT_NACK_CAUSES,
+ NM_ATT_NY1,
+ NM_ATT_OPER_STATE,
+ NM_ATT_OVERL_PERIOD,
+ NM_ATT_PHYS_CONF,
+ NM_ATT_POWER_CLASS,
+ NM_ATT_POWER_THRESH,
+ NM_ATT_PROB_CAUSE,
+ NM_ATT_RACH_B_THRESH,
+ NM_ATT_LDAVG_SLOTS,
+ NM_ATT_RAD_SUBC,
+ NM_ATT_RF_MAXPOWR_R,
+ NM_ATT_SITE_INPUTS,
+ NM_ATT_SITE_OUTPUTS,
+ NM_ATT_SOURCE, /* 0x30 */
+ NM_ATT_SPEC_PROB,
+ NM_ATT_START_TIME,
+ NM_ATT_T200,
+ NM_ATT_TEI,
+ NM_ATT_TEST_DUR,
+ NM_ATT_TEST_NO,
+ NM_ATT_TEST_REPORT,
+ NM_ATT_VSWR_THRESH,
+ NM_ATT_WINDOW_SIZE,
+ /* Res */
+ NM_ATT_BS11_RSSI_OFFS = 0x3d,
+ NM_ATT_BS11_TXPWR = 0x3e,
+ NM_ATT_BS11_DIVERSITY = 0x3f,
+ /* Res */
+ NM_ATT_TSC = 0x40,
+ NM_ATT_SW_CONFIG,
+ NM_ATT_SW_DESCR,
+ NM_ATT_SEVERITY,
+ NM_ATT_GET_ARI,
+ NM_ATT_HW_CONF_CHG,
+ NM_ATT_OUTST_ALARM,
+ NM_ATT_FILE_DATA,
+ NM_ATT_MEAS_RES,
+ NM_ATT_MEAS_TYPE,
+
+ NM_ATT_BS11_ESN_FW_CODE_NO = 0x4c,
+ NM_ATT_BS11_ESN_HW_CODE_NO = 0x4f,
+
+ NM_ATT_BS11_ESN_PCB_SERIAL = 0x55,
+ NM_ATT_BS11_EXCESSIVE_DISTANCE = 0x58,
+
+ NM_ATT_BS11_ALL_TEST_CATG = 0x60,
+ NM_ATT_BS11_BTSLS_HOPPING,
+ NM_ATT_BS11_CELL_ALLOC_NR,
+ NM_ATT_BS11_CELL_GLOBAL_ID,
+ NM_ATT_BS11_ENA_INTERF_CLASS = 0x66,
+ NM_ATT_BS11_ENA_INT_INTEC_HANDO = 0x67,
+ NM_ATT_BS11_ENA_INT_INTRC_HANDO = 0x68,
+ NM_ATT_BS11_ENA_MS_PWR_CTRL = 0x69,
+ NM_ATT_BS11_ENA_PWR_BDGT_HO = 0x6a,
+ NM_ATT_BS11_ENA_PWR_CTRL_RLFW = 0x6b,
+ NM_ATT_BS11_ENA_RXLEV_HO = 0x6c,
+ NM_ATT_BS11_ENA_RXQUAL_HO = 0x6d,
+ NM_ATT_BS11_FACCH_QUAL = 0x6e,
+
+ NM_ATT_IPACC_DST_IP = 0x80,
+ NM_ATT_IPACC_DST_IP_PORT = 0x81,
+ NM_ATT_IPACC_SSRC = 0x82,
+ NM_ATT_IPACC_RTP_PAYLD_TYPE = 0x83,
+ NM_ATT_IPACC_BASEB_ID = 0x84,
+ NM_ATT_IPACC_STREAM_ID = 0x85,
+ NM_ATT_IPACC_NV_FLAGS = 0x86,
+ NM_ATT_IPACC_FREQ_CTRL = 0x87,
+ NM_ATT_IPACC_PRIM_OML_CFG = 0x88,
+ NM_ATT_IPACC_SEC_OML_CFG = 0x89,
+ NM_ATT_IPACC_IP_IF_CFG = 0x8a, /* IP interface */
+ NM_ATT_IPACC_IP_GW_CFG = 0x8b, /* IP gateway */
+ NM_ATT_IPACC_IN_SERV_TIME = 0x8c,
+ NM_ATT_IPACC_TRX_BTS_ASS = 0x8d,
+ NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */
+ NM_ATT_IPACC_PAGING_CFG = 0x8f,
+ NM_ATT_IPACC_FILE_DATA = 0x90,
+ NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */
+ NM_ATT_IPACC_PARENT_UNIT_ID = 0x92,
+ NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts-<mac-as-string> */
+ NM_ATT_IPACC_SNMP_CFG = 0x94,
+ NM_ATT_IPACC_PRIM_OML_CFG_LIST = 0x95,
+ NM_ATT_IPACC_PRIM_OML_FB_TOUT = 0x96,
+ NM_ATT_IPACC_CUR_SW_CFG = 0x97,
+ NM_ATT_IPACC_TIMING_BUS = 0x98,
+ NM_ATT_IPACC_CGI = 0x99,
+ NM_ATT_IPACC_RAC = 0x9a,
+ NM_ATT_IPACC_OBJ_VERSION = 0x9b,
+ NM_ATT_IPACC_GPRS_PAGING_CFG = 0x9c,
+ NM_ATT_IPACC_NSEI = 0x9d,
+ NM_ATT_IPACC_BVCI = 0x9e,
+ NM_ATT_IPACC_NSVCI = 0x9f,
+ NM_ATT_IPACC_NS_CFG = 0xa0,
+ NM_ATT_IPACC_BSSGP_CFG = 0xa1,
+ NM_ATT_IPACC_NS_LINK_CFG = 0xa2,
+ NM_ATT_IPACC_RLC_CFG = 0xa3,
+ NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4,
+ NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5,
+ NM_ATT_IPACC_TIB_CONTROL = 0xa6,
+ NM_ATT_IPACC_SUPP_FEATURES = 0xa7,
+ NM_ATT_IPACC_CODING_SCHEMES = 0xa8,
+ NM_ATT_IPACC_RLC_CFG_2 = 0xa9,
+ NM_ATT_IPACC_HEARTB_TOUT = 0xaa,
+ NM_ATT_IPACC_UPTIME = 0xab,
+ NM_ATT_IPACC_RLC_CFG_3 = 0xac,
+ NM_ATT_IPACC_SSL_CFG = 0xad,
+ NM_ATT_IPACC_SEC_POSSIBLE = 0xae,
+ NM_ATT_IPACC_IML_SSL_STATE = 0xaf,
+ NM_ATT_IPACC_REVOC_DATE = 0xb0,
+
+
+ NM_ATT_BS11_RF_RES_IND_PER = 0x8f,
+
+ NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90,
+ NM_ATT_BS11_ABIS_EXT_TIME = 0x91,
+ NM_ATT_BS11_TIMER_HO_REQUEST = 0x92,
+ NM_ATT_BS11_TIMER_NCELL = 0x93,
+ NM_ATT_BS11_TSYNC = 0x94,
+ NM_ATT_BS11_TTRAU = 0x95,
+ NM_ATT_BS11_EMRG_CFG_MEMBER = 0x9b,
+ NM_ATT_BS11_TRX_AREA = 0x9f,
+
+ NM_ATT_BS11_BCCH_RECONF = 0xd7,
+ NM_ATT_BS11_BIT_ERR_THESH = 0xa0,
+ NM_ATT_BS11_BOOT_SW_VERS = 0xa1,
+ NM_ATT_BS11_CCLK_ACCURACY = 0xa3,
+ NM_ATT_BS11_CCLK_TYPE = 0xa4,
+ NM_ATT_BS11_INP_IMPEDANCE = 0xaa,
+ NM_ATT_BS11_L1_PROT_TYPE = 0xab,
+ NM_ATT_BS11_LINE_CFG = 0xac,
+ NM_ATT_BS11_LI_PORT_1 = 0xad,
+ NM_ATT_BS11_LI_PORT_2 = 0xae,
+
+ NM_ATT_BS11_L1_REM_ALM_TYPE = 0xb0,
+ NM_ATT_BS11_SW_LOAD_INTENDED = 0xbb,
+ NM_ATT_BS11_SW_LOAD_SAFETY = 0xbc,
+ NM_ATT_BS11_SW_LOAD_STORED = 0xbd,
+
+ NM_ATT_BS11_VENDOR_NAME = 0xc1,
+ NM_ATT_BS11_HOPPING_MODE = 0xc5,
+ NM_ATT_BS11_LMT_LOGON_SESSION = 0xc6,
+ NM_ATT_BS11_LMT_LOGIN_TIME = 0xc7,
+ NM_ATT_BS11_LMT_USER_ACC_LEV = 0xc8,
+ NM_ATT_BS11_LMT_USER_NAME = 0xc9,
+
+ NM_ATT_BS11_L1_CONTROL_TS = 0xd8,
+ NM_ATT_BS11_RADIO_MEAS_GRAN = 0xdc, /* in SACCH multiframes */
+ NM_ATT_BS11_RADIO_MEAS_REP = 0xdd,
+
+ NM_ATT_BS11_SH_LAPD_INT_TIMER = 0xe8,
+
+ NM_ATT_BS11_BTS_STATE = 0xf0,
+ NM_ATT_BS11_E1_STATE = 0xf1,
+ NM_ATT_BS11_PLL = 0xf2,
+ NM_ATT_BS11_RX_OFFSET = 0xf3,
+ NM_ATT_BS11_ANT_TYPE = 0xf4,
+ NM_ATT_BS11_PLL_MODE = 0xfc,
+ NM_ATT_BS11_PASSWORD = 0xfd,
+};
+#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE
+
+/* Section 9.4.4: Administrative State */
+enum abis_nm_adm_state {
+ NM_STATE_LOCKED = 0x01,
+ NM_STATE_UNLOCKED = 0x02,
+ NM_STATE_SHUTDOWN = 0x03,
+ NM_STATE_NULL = 0xff,
+};
+
+/* Section 9.4.7: Administrative State */
+enum abis_nm_avail_state {
+ NM_AVSTATE_IN_TEST = 1,
+ NM_AVSTATE_POWER_OFF = 2,
+ NM_AVSTATE_OFF_LINE = 3,
+ NM_AVSTATE_DEPENDENCY = 5,
+ NM_AVSTATE_DEGRADED = 6,
+ NM_AVSTATE_NOT_INSTALLED= 7,
+ NM_AVSTATE_OK = 0xff,
+};
+
+enum abis_nm_op_state {
+ NM_OPSTATE_DISABLED = 1,
+ NM_OPSTATE_ENABLED = 2,
+ NM_OPSTATE_NULL = 0xff,
+};
+
+/* Section 9.4.13: Channel Combination */
+enum abis_nm_chan_comb {
+ NM_CHANC_TCHFull = 0x00, /* TCH/F + TCH/H + SACCH/TF */
+ NM_CHANC_TCHHalf = 0x01, /* TCH/H(0,1) + FACCH/H(0,1) +
+ SACCH/TH(0,1) */
+ NM_CHANC_TCHHalf2 = 0x02, /* TCH/H(0) + FACCH/H(0) + SACCH/TH(0) +
+ TCH/H(1) */
+ NM_CHANC_SDCCH = 0x03, /* SDCCH/8 + SACCH/8 */
+ NM_CHANC_mainBCCH = 0x04, /* FCCH + SCH + BCCH + CCCH */
+ NM_CHANC_BCCHComb = 0x05, /* FCCH + SCH + BCCH + CCCH + SDCCH/4 +
+ SACCH/C4 */
+ NM_CHANC_BCCH = 0x06, /* BCCH + CCCH */
+ NM_CHANC_BCCH_CBCH = 0x07, /* CHANC_BCCHComb + CBCH */
+ NM_CHANC_SDCCH_CBCH = 0x08, /* CHANC_SDCCH8 + CBCH */
+ /* ip.access */
+ NM_CHANC_IPAC_bPDCH = 0x0b, /* PBCCH + PCCCH + PDTCH/F + PACCH/F +
+ PTCCH/F */
+ NM_CHANC_IPAC_cPDCH = 0x0c, /* PBCCH + PDTCH/F + PACCH/F + PTCCH/F */
+ NM_CHANC_IPAC_PDCH = 0x0d, /* PDTCH/F + PACCH/F + PTCCH/F */
+ NM_CHANC_IPAC_TCHFull_PDCH = 0x80,
+ NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81,
+};
+
+/* Section 9.4.16: Event Type */
+enum abis_nm_event_type {
+ NM_EVT_COMM_FAIL = 0x00,
+ NM_EVT_QOS_FAIL = 0x01,
+ NM_EVT_PROC_FAIL = 0x02,
+ NM_EVT_EQUIP_FAIL = 0x03,
+ NM_EVT_ENV_FAIL = 0x04,
+};
+
+/* Section: 9.4.63: Perceived Severity */
+enum abis_nm_severity {
+ NM_SEVER_CEASED = 0x00,
+ NM_SEVER_CRITICAL = 0x01,
+ NM_SEVER_MAJOR = 0x02,
+ NM_SEVER_MINOR = 0x03,
+ NM_SEVER_WARNING = 0x04,
+ NM_SEVER_INDETERMINATE = 0x05,
+};
+
+/* Section 9.4.43: Probable Cause Type */
+enum abis_nm_pcause_type {
+ NM_PCAUSE_T_X721 = 0x01,
+ NM_PCAUSE_T_GSM = 0x02,
+ NM_PCAUSE_T_MANUF = 0x03,
+};
+
+/* Section 9.4.36: NACK Causes */
+enum abis_nm_nack_cause {
+ /* General Nack Causes */
+ NM_NACK_INCORR_STRUCT = 0x01,
+ NM_NACK_MSGTYPE_INVAL = 0x02,
+ NM_NACK_OBJCLASS_INVAL = 0x05,
+ NM_NACK_OBJCLASS_NOTSUPP = 0x06,
+ NM_NACK_BTSNR_UNKN = 0x07,
+ NM_NACK_TRXNR_UNKN = 0x08,
+ NM_NACK_OBJINST_UNKN = 0x09,
+ NM_NACK_ATTRID_INVAL = 0x0c,
+ NM_NACK_ATTRID_NOTSUPP = 0x0d,
+ NM_NACK_PARAM_RANGE = 0x0e,
+ NM_NACK_ATTRLIST_INCONSISTENT = 0x0f,
+ NM_NACK_SPEC_IMPL_NOTSUPP = 0x10,
+ NM_NACK_CANT_PERFORM = 0x11,
+ /* Specific Nack Causes */
+ NM_NACK_RES_NOTIMPL = 0x19,
+ NM_NACK_RES_NOTAVAIL = 0x1a,
+ NM_NACK_FREQ_NOTAVAIL = 0x1b,
+ NM_NACK_TEST_NOTSUPP = 0x1c,
+ NM_NACK_CAPACITY_RESTR = 0x1d,
+ NM_NACK_PHYSCFG_NOTPERFORM = 0x1e,
+ NM_NACK_TEST_NOTINIT = 0x1f,
+ NM_NACK_PHYSCFG_NOTRESTORE = 0x20,
+ NM_NACK_TEST_NOSUCH = 0x21,
+ NM_NACK_TEST_NOSTOP = 0x22,
+ NM_NACK_MSGINCONSIST_PHYSCFG = 0x23,
+ NM_NACK_FILE_INCOMPLETE = 0x25,
+ NM_NACK_FILE_NOTAVAIL = 0x26,
+ NM_NACK_FILE_NOTACTIVATE = 0x27,
+ NM_NACK_REQ_NOT_GRANT = 0x28,
+ NM_NACK_WAIT = 0x29,
+ NM_NACK_NOTH_REPORT_EXIST = 0x2a,
+ NM_NACK_MEAS_NOTSUPP = 0x2b,
+ NM_NACK_MEAS_NOTSTART = 0x2c,
+};
+
+/* Section 9.4.1 */
+struct abis_nm_channel {
+ uint8_t attrib;
+ uint8_t bts_port;
+ uint8_t timeslot;
+ uint8_t subslot;
+} __attribute__ ((packed));
+
+/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */
+enum abis_bs11_objtype {
+ BS11_OBJ_ALCO = 0x01,
+ BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */
+ BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */
+ BS11_OBJ_CCLK = 0x04,
+ BS11_OBJ_GPSU = 0x06,
+ BS11_OBJ_LI = 0x07,
+ BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/
+};
+
+enum abis_bs11_trx_power {
+ BS11_TRX_POWER_GSM_2W = 0x06,
+ BS11_TRX_POWER_GSM_250mW= 0x07,
+ BS11_TRX_POWER_GSM_80mW = 0x08,
+ BS11_TRX_POWER_GSM_30mW = 0x09,
+ BS11_TRX_POWER_DCS_3W = 0x0a,
+ BS11_TRX_POWER_DCS_1W6 = 0x0b,
+ BS11_TRX_POWER_DCS_500mW= 0x0c,
+ BS11_TRX_POWER_DCS_160mW= 0x0d,
+};
+
+enum abis_bs11_li_pll_mode {
+ BS11_LI_PLL_LOCKED = 2,
+ BS11_LI_PLL_STANDALONE = 3,
+};
+
+enum abis_bs11_line_cfg {
+ BS11_LINE_CFG_STAR = 0x00,
+ BS11_LINE_CFG_MULTIDROP = 0x01,
+ BS11_LINE_CFG_LOOP = 0x02,
+};
+
+enum abis_bs11_phase {
+ BS11_STATE_SOFTWARE_RQD = 0x01,
+ BS11_STATE_LOAD_SMU_INTENDED = 0x11,
+ BS11_STATE_LOAD_SMU_SAFETY = 0x21,
+ BS11_STATE_LOAD_FAILED = 0x31,
+ BS11_STATE_LOAD_DIAGNOSTIC = 0x41,
+ BS11_STATE_WARM_UP = 0x51,
+ BS11_STATE_WARM_UP_2 = 0x52,
+ BS11_STATE_WAIT_MIN_CFG = 0x62,
+ BS11_STATE_MAINTENANCE = 0x72,
+ BS11_STATE_LOAD_MBCCU = 0x92,
+ BS11_STATE_WAIT_MIN_CFG_2 = 0xA2,
+ BS11_STATE_NORMAL = 0x03,
+ BS11_STATE_ABIS_LOAD = 0x13,
+};
+
+enum abis_nm_ipacc_test_no {
+ NM_IPACC_TESTNO_RLOOP_ANT = 0x01,
+ NM_IPACC_TESTNO_RLOOP_XCVR = 0x02,
+ NM_IPACC_TESTNO_FUNC_OBJ = 0x03,
+ NM_IPACC_TESTNO_CHAN_USAGE = 0x40,
+ NM_IPACC_TESTNO_BCCH_CHAN_USAGE = 0x41,
+ NM_IPACC_TESTNO_FREQ_SYNC = 0x42,
+ NM_IPACC_TESTNO_BCCH_INFO = 0x43,
+ NM_IPACC_TESTNO_TX_BEACON = 0x44,
+ NM_IPACC_TESTNO_SYSINFO_MONITOR = 0x45,
+ NM_IPACC_TESTNO_BCCCH_MONITOR = 0x46,
+};
+
+/* first byte after length inside NM_ATT_TEST_REPORT */
+enum abis_nm_ipacc_test_res {
+ NM_IPACC_TESTRES_SUCCESS = 0,
+ NM_IPACC_TESTRES_TIMEOUT = 1,
+ NM_IPACC_TESTRES_NO_CHANS = 2,
+ NM_IPACC_TESTRES_PARTIAL = 3,
+ NM_IPACC_TESTRES_STOPPED = 4,
+};
+
+/* internal IE inside NM_ATT_TEST_REPORT */
+enum abis_nm_ipacc_testres_ie {
+ NM_IPACC_TR_IE_FREQ_ERR_LIST = 3,
+ NM_IPACC_TR_IE_CHAN_USAGE = 4,
+ NM_IPACC_TR_IE_BCCH_INFO = 6,
+ NM_IPACC_TR_IE_RESULT_DETAILS = 8,
+ NM_IPACC_TR_IE_FREQ_ERR = 18,
+};
+
+enum ipac_eie {
+ NM_IPAC_EIE_ARFCN_WHITE = 0x01,
+ NM_IPAC_EIE_ARFCH_BLACK = 0x02,
+ NM_IPAC_EIE_FREQ_ERR_LIST = 0x03,
+ NM_IPAC_EIE_CHAN_USE_LIST = 0x04,
+ NM_IPAC_EIE_BCCH_INFO_TYPE = 0x05,
+ NM_IPAC_EIE_BCCH_INFO = 0x06,
+ NM_IPAC_EIE_CONFIG = 0x07,
+ NM_IPAC_EIE_RES_DETAILS = 0x08,
+ NM_IPAC_EIE_RXLEV_THRESH = 0x09,
+ NM_IPAC_EIE_FREQ_SYNC_OPTS = 0x0a,
+ NM_IPAC_EIE_MAC_ADDR = 0x0b,
+ NM_IPAC_EIE_HW_SW_COMPAT_NR = 0x0c,
+ NM_IPAC_EIE_MANUF_SER_NR = 0x0d,
+ NM_IPAC_EIE_OEM_ID = 0x0e,
+ NM_IPAC_EIE_DATE_TIME_MANUF = 0x0f,
+ NM_IPAC_EIE_DATE_TIME_CALIB = 0x10,
+ NM_IPAC_EIE_BEACON_INFO = 0x11,
+ NM_IPAC_EIE_FREQ_ERR = 0x12,
+ /* FIXME */
+ NM_IPAC_EIE_FREQ_BANDS = 0x1c,
+ NM_IPAC_EIE_MAX_TA = 0x1d,
+ NM_IPAC_EIE_CIPH_ALGOS = 0x1e,
+ NM_IPAC_EIE_CHAN_TYPES = 0x1f,
+ NM_IPAC_EIE_CHAN_MODES = 0x20,
+ NM_IPAC_EIE_GPRS_CODING = 0x21,
+ NM_IPAC_EIE_RTP_FEATURES = 0x22,
+ NM_IPAC_EIE_RSL_FEATURES = 0x23,
+ NM_IPAC_EIE_BTS_HW_CLASS = 0x24,
+ NM_IPAC_EIE_BTS_ID = 0x25,
+};
+
+enum ipac_bcch_info_type {
+ IPAC_BINF_RXLEV = (1 << 8),
+ IPAC_BINF_RXQUAL = (1 << 9),
+ IPAC_BINF_FREQ_ERR_QUAL = (1 << 10),
+ IPAC_BINF_FRAME_OFFSET = (1 << 11),
+ IPAC_BINF_FRAME_NR_OFFSET = (1 << 12),
+ IPAC_BINF_BSIC = (1 << 13),
+ IPAC_BINF_CGI = (1 << 14),
+ IPAC_BINF_NEIGH_BA_SI2 = (1 << 15),
+ IPAC_BINF_NEIGH_BA_SI2bis = (1 << 0),
+ IPAC_BINF_NEIGH_BA_SI2ter = (1 << 1),
+ IPAC_BINF_CELL_ALLOC = (1 << 2),
+};
+
+#endif /* PROTO_GSM_12_21_H */
diff --git a/src/shared/libosmocore/include/osmocore/rate_ctr.h b/src/shared/libosmocore/include/osmocore/rate_ctr.h
new file mode 100644
index 00000000..f887d9a7
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/rate_ctr.h
@@ -0,0 +1,81 @@
+#ifndef _RATE_CTR_H
+#define _RATE_CTR_H
+
+#include <stdint.h>
+
+#include <osmocore/linuxlist.h>
+
+#define RATE_CTR_INTV_NUM 4
+
+enum rate_ctr_intv {
+ RATE_CTR_INTV_SEC,
+ RATE_CTR_INTV_MIN,
+ RATE_CTR_INTV_HOUR,
+ RATE_CTR_INTV_DAY,
+};
+
+/* for each of the intervals, we keep the following values */
+struct rate_ctr_per_intv {
+ uint64_t last;
+ uint64_t rate;
+};
+
+/* for each actual value, we keep the following data */
+struct rate_ctr {
+ uint64_t current;
+ struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM];
+};
+
+struct rate_ctr_desc {
+ const char *name;
+ const char *description;
+};
+
+/* Describe a counter group class */
+struct rate_ctr_group_desc {
+ /* The prefix to the name of all counters in this group */
+ const char *group_name_prefix;
+ /* The human-readable description of the group */
+ const char *group_description;
+ /* The number of counters in this group */
+ const unsigned int num_ctr;
+ /* Pointer to array of counter names */
+ const struct rate_ctr_desc *ctr_desc;
+};
+
+/* One instance of a counter group class */
+struct rate_ctr_group {
+ /* Linked list of all counter groups in the system */
+ struct llist_head list;
+ /* Pointer to the counter group class */
+ const struct rate_ctr_group_desc *desc;
+ /* The index of this ctr_group within its class */
+ unsigned int idx;
+ /* Actual counter structures below */
+ struct rate_ctr ctr[0];
+};
+
+/* Allocate a new group of counters according to description */
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+ const struct rate_ctr_group_desc *desc,
+ unsigned int idx);
+
+/* Free the memory for the specified group of counters */
+void rate_ctr_group_free(struct rate_ctr_group *grp);
+
+/* Add a number to the counter */
+void rate_ctr_add(struct rate_ctr *ctr, int inc);
+
+/* Increment the counter by 1 */
+static inline void rate_ctr_inc(struct rate_ctr *ctr)
+{
+ rate_ctr_add(ctr, 1);
+}
+
+/* Initialize the counter module */
+int rate_ctr_init(void *tall_ctx);
+
+struct vty;
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg);
+#endif /* RATE_CTR_H */
diff --git a/src/shared/libosmocore/include/osmocore/rsl.h b/src/shared/libosmocore/include/osmocore/rsl.h
new file mode 100644
index 00000000..99b90d68
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/rsl.h
@@ -0,0 +1,32 @@
+#ifndef _OSMOCORE_RSL_H
+#define _OSMOCORE_RSL_H
+
+#include <stdint.h>
+#include <osmocore/utils.h>
+#include <osmocore/protocol/gsm_08_58.h>
+
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type);
+
+extern const struct tlv_definition rsl_att_tlvdef;
+#define rsl_tlv_parse(dec, buf, len) \
+ tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
+
+/* encode channel number as per Section 9.3.1 */
+uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot);
+/* decode channel number as per Section 9.3.1 */
+int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot);
+
+const char *rsl_err_name(uint8_t err);
+const char *rsl_rlm_cause_name(uint8_t err);
+
+/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
+
+/* Push a RSL RLL header with L3_INFO IE */
+void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
+ uint8_t link_id, int transparent);
+
+/* Allocate msgb and fill with simple RSL RLL header */
+struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr,
+ uint8_t link_id, int transparent);
+#endif /* _OSMOCORE_RSL_H */
diff --git a/src/shared/libosmocore/include/osmocore/rxlev_stat.h b/src/shared/libosmocore/include/osmocore/rxlev_stat.h
new file mode 100644
index 00000000..415509dc
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/rxlev_stat.h
@@ -0,0 +1,22 @@
+#ifndef _OSMOCORE_RXLEV_STATS_H
+#define _OSMOCORE_RXLEV_STATS_H
+
+#define NUM_RXLEVS 32
+#define NUM_ARFCNS 1024
+
+struct rxlev_stats {
+ /* the maximum number of ARFCN's is 1024, and there are 32 RxLevels,
+ * so in we keep one 1024bit-bitvec for each RxLev */
+ uint8_t rxlev_buckets[NUM_RXLEVS][NUM_ARFCNS/8];
+};
+
+void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev);
+
+/* get the next ARFCN that has the specified Rxlev */
+int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn);
+
+void rxlev_stat_reset(struct rxlev_stats *st);
+
+void rxlev_stat_dump(const struct rxlev_stats *st);
+
+#endif /* _OSMOCORE_RXLEV_STATS_H */
diff --git a/src/shared/libosmocore/include/osmocore/select.h b/src/shared/libosmocore/include/osmocore/select.h
new file mode 100644
index 00000000..2d8b3ec0
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/select.h
@@ -0,0 +1,22 @@
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#include "linuxlist.h"
+
+#define BSC_FD_READ 0x0001
+#define BSC_FD_WRITE 0x0002
+#define BSC_FD_EXCEPT 0x0004
+
+struct bsc_fd {
+ struct llist_head list;
+ int fd;
+ unsigned int when;
+ int (*cb)(struct bsc_fd *fd, unsigned int what);
+ void *data;
+ unsigned int priv_nr;
+};
+
+int bsc_register_fd(struct bsc_fd *fd);
+void bsc_unregister_fd(struct bsc_fd *fd);
+int bsc_select_main(int polling);
+#endif /* _BSC_SELECT_H */
diff --git a/src/shared/libosmocore/include/osmocore/signal.h b/src/shared/libosmocore/include/osmocore/signal.h
new file mode 100644
index 00000000..02d83d2e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/signal.h
@@ -0,0 +1,15 @@
+#ifndef OSMOCORE_SIGNAL_H
+#define OSMOCORE_SIGNAL_H
+
+typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data);
+
+
+/* Management */
+int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+
+/* Dispatch */
+void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data);
+
+#endif /* OSMOCORE_SIGNAL_H */
diff --git a/src/shared/libosmocore/include/osmocore/statistics.h b/src/shared/libosmocore/include/osmocore/statistics.h
new file mode 100644
index 00000000..1d56054a
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/statistics.h
@@ -0,0 +1,31 @@
+#ifndef _STATISTICS_H
+#define _STATISTICS_H
+
+struct counter {
+ struct llist_head list;
+ const char *name;
+ const char *description;
+ unsigned long value;
+};
+
+static inline void counter_inc(struct counter *ctr)
+{
+ ctr->value++;
+}
+
+static inline unsigned long counter_get(struct counter *ctr)
+{
+ return ctr->value;
+}
+
+static inline void counter_reset(struct counter *ctr)
+{
+ ctr->value = 0;
+}
+
+struct counter *counter_alloc(const char *name);
+void counter_free(struct counter *ctr);
+
+int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data);
+
+#endif /* _STATISTICS_H */
diff --git a/src/shared/libosmocore/include/osmocore/talloc.h b/src/shared/libosmocore/include/osmocore/talloc.h
new file mode 100644
index 00000000..f7f7643b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/talloc.h
@@ -0,0 +1,192 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/*
+ Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions
+
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Stefan Metzmacher 2006
+
+ ** NOTE! The following LGPL license applies to the talloc
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define HAVE_VA_COPY
+
+/* this is only needed for compatibility with the old talloc */
+typedef void TALLOC_CTX;
+
+/*
+ this uses a little trick to allow __LINE__ to be stringified
+*/
+#ifndef __location__
+#define __TALLOC_STRING_LINE1__(s) #s
+#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s)
+#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__)
+#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
+#endif
+
+#ifndef TALLOC_DEPRECATED
+#define TALLOC_DEPRECATED 0
+#endif
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns. a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+/* try to make talloc_set_destructor() and talloc_steal() type safe,
+ if we have a recent gcc */
+#if (__GNUC__ >= 3)
+#define _TALLOC_TYPEOF(ptr) __typeof__(ptr)
+#define talloc_set_destructor(ptr, function) \
+ do { \
+ int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \
+ _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \
+ } while(0)
+/* this extremely strange macro is to avoid some braindamaged warning
+ stupidity in gcc 4.1.x */
+#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; })
+#else
+#define talloc_set_destructor(ptr, function) \
+ _talloc_set_destructor((ptr), (int (*)(void *))(function))
+#define _TALLOC_TYPEOF(ptr) void *
+#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
+#endif
+
+#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
+#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
+
+/* useful macros for creating type checked pointers */
+#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
+#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
+#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
+
+#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
+
+#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
+
+#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
+#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
+#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx))
+
+#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
+#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
+
+#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
+
+#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
+#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
+#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__)
+
+#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
+
+#if TALLOC_DEPRECATED
+#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
+#define talloc_p(ctx, type) talloc(ctx, type)
+#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count)
+#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count)
+#define talloc_destroy(ctx) talloc_free(ctx)
+#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a))
+#endif
+
+#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
+
+/* The following definitions come from talloc.c */
+void *_talloc(const void *context, size_t size);
+void *talloc_pool(const void *context, size_t size);
+void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *));
+int talloc_increase_ref_count(const void *ptr);
+size_t talloc_reference_count(const void *ptr);
+void *_talloc_reference(const void *context, const void *ptr);
+int talloc_unlink(const void *context, void *ptr);
+const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+void talloc_set_name_const(const void *ptr, const char *name);
+void *talloc_named(const void *context, size_t size,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+void *talloc_named_const(const void *context, size_t size, const char *name);
+const char *talloc_get_name(const void *ptr);
+void *talloc_check_name(const void *ptr, const char *name);
+void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location);
+void *talloc_parent(const void *ptr);
+const char *talloc_parent_name(const void *ptr);
+void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
+int talloc_free(void *ptr);
+void talloc_free_children(void *ptr);
+void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
+void *_talloc_steal(const void *new_ctx, const void *ptr);
+void *_talloc_move(const void *new_ctx, const void *pptr);
+size_t talloc_total_size(const void *ptr);
+size_t talloc_total_blocks(const void *ptr);
+void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
+ void (*callback)(const void *ptr,
+ int depth, int max_depth,
+ int is_ref,
+ void *private_data),
+ void *private_data);
+void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
+void talloc_report_full(const void *ptr, FILE *f);
+void talloc_report(const void *ptr, FILE *f);
+void talloc_enable_null_tracking(void);
+void talloc_disable_null_tracking(void);
+void talloc_enable_leak_report(void);
+void talloc_enable_leak_report_full(void);
+void *_talloc_zero(const void *ctx, size_t size, const char *name);
+void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
+void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
+void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
+void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
+void *talloc_autofree_context(void);
+size_t talloc_get_size(const void *ctx);
+void *talloc_find_parent_byname(const void *ctx, const char *name);
+void talloc_show_parents(const void *context, FILE *file);
+int talloc_is_parent(const void *context, const void *ptr);
+
+char *talloc_strdup(const void *t, const char *p);
+char *talloc_strdup_append(char *s, const char *a);
+char *talloc_strdup_append_buffer(char *s, const char *a);
+
+char *talloc_strndup(const void *t, const char *p, size_t n);
+char *talloc_strndup_append(char *s, const char *a, size_t n);
+char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
+
+char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+void talloc_set_abort_fn(void (*abort_fn)(const char *reason));
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/timer.h b/src/shared/libosmocore/include/osmocore/timer.h
new file mode 100644
index 00000000..fee888bf
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/timer.h
@@ -0,0 +1,72 @@
+/*
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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.
+ *
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <sys/time.h>
+
+#include "linuxlist.h"
+
+/**
+ * Timer management:
+ * - Create a struct timer_list
+ * - Fill out timeout and use add_timer or
+ * use schedule_timer to schedule a timer in
+ * x seconds and microseconds from now...
+ * - Use del_timer to remove the timer
+ *
+ * Internally:
+ * - We hook into select.c to give a timeval of the
+ * nearest timer. On already passed timers we give
+ * it a 0 to immediately fire after the select
+ * - update_timers will call the callbacks and remove
+ * the timers.
+ *
+ */
+struct timer_list {
+ struct llist_head entry;
+ struct timeval timeout;
+ unsigned int active : 1;
+ unsigned int handled : 1;
+ unsigned int in_list : 1;
+
+ void (*cb)(void*);
+ void *data;
+};
+
+/**
+ * timer management
+ */
+void bsc_add_timer(struct timer_list *timer);
+void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds);
+void bsc_del_timer(struct timer_list *timer);
+int bsc_timer_pending(struct timer_list *timer);
+
+
+/**
+ * internal timer list management
+ */
+struct timeval *bsc_nearest_timer();
+void bsc_prepare_timers();
+int bsc_update_timers();
+int bsc_timer_check(void);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/tlv.h b/src/shared/libosmocore/include/osmocore/tlv.h
new file mode 100644
index 00000000..4cfce872
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/tlv.h
@@ -0,0 +1,245 @@
+#ifndef _TLV_H
+#define _TLV_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocore/msgb.h>
+
+/* Terminology / wording
+ tag length value (in bits)
+
+ V - - 8
+ LV - 8 N * 8
+ TLV 8 8 N * 8
+ TL16V 8 16 N * 8
+ TLV16 8 8 N * 16
+ TvLV 8 8/16 N * 8
+
+*/
+
+#define LV_GROSS_LEN(x) (x+1)
+#define TLV_GROSS_LEN(x) (x+2)
+#define TLV16_GROSS_LEN(x) ((2*x)+2)
+#define TL16V_GROSS_LEN(x) (x+3)
+#define L16TV_GROSS_LEN(x) (x+3)
+
+#define TVLV_MAX_ONEBYTE 0x7f
+
+static inline uint16_t TVLV_GROSS_LEN(uint16_t len)
+{
+ if (len <= TVLV_MAX_ONEBYTE)
+ return TLV_GROSS_LEN(len);
+ else
+ return TL16V_GROSS_LEN(len);
+}
+
+/* TLV generation */
+
+static inline uint8_t *lv_put(uint8_t *buf, uint8_t len,
+ const uint8_t *val)
+{
+ *buf++ = len;
+ memcpy(buf, val, len);
+ return buf + len;
+}
+
+static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,
+ const uint8_t *val)
+{
+ *buf++ = tag;
+ *buf++ = len;
+ memcpy(buf, val, len);
+ return buf + len;
+}
+
+static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,
+ const uint16_t *val)
+{
+ *buf++ = tag;
+ *buf++ = len;
+ memcpy(buf, val, len*2);
+ return buf + len*2;
+}
+
+static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,
+ const uint8_t *val)
+{
+ *buf++ = tag;
+ *buf++ = len >> 8;
+ *buf++ = len & 0xff;
+ memcpy(buf, val, len);
+ return buf + len*2;
+}
+
+static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,
+ const uint8_t *val)
+{
+ uint8_t *ret;
+
+ if (len <= TVLV_MAX_ONEBYTE) {
+ ret = tlv_put(buf, tag, len, val);
+ buf[1] |= 0x80;
+ } else
+ ret = tl16v_put(buf, tag, len, val);
+
+ return ret;
+}
+
+static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val)
+{
+ uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
+ return tlv16_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len,
+ const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));
+ return tl16v_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,
+ const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));
+ return tvlv_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,
+ const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));
+
+ *buf++ = len >> 8;
+ *buf++ = len & 0xff;
+ *buf++ = tag;
+ memcpy(buf, val, len);
+ return buf + len;
+}
+
+static inline uint8_t *v_put(uint8_t *buf, uint8_t val)
+{
+ *buf++ = val;
+ return buf;
+}
+
+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag,
+ uint8_t val)
+{
+ *buf++ = tag;
+ *buf++ = val;
+ return buf;
+}
+
+/* 'val' is still in host byte order! */
+static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag,
+ uint16_t val)
+{
+ *buf++ = tag;
+ *buf++ = val >> 8;
+ *buf++ = val & 0xff;
+ return buf;
+}
+
+static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));
+ return lv_put(buf, len, val);
+}
+
+static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)
+{
+ uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
+ return tlv_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val)
+{
+ uint8_t *buf = msgb_put(msg, 2);
+ return tv_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val)
+{
+ uint8_t *buf = msgb_put(msg, 1);
+ return v_put(buf, val);
+}
+
+static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)
+{
+ uint8_t *buf = msgb_put(msg, 3);
+ return tv16_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)
+{
+ uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
+ return tlv_put(buf, tag, len, val);
+}
+
+static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)
+{
+ uint8_t *buf = msgb_push(msg, 2);
+ return tv_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)
+{
+ uint8_t *buf = msgb_push(msg, 3);
+ return tv16_put(buf, tag, val);
+}
+
+static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,
+ const uint8_t *val)
+{
+ uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len));
+ return tvlv_put(buf, tag, len, val);
+}
+
+/* TLV parsing */
+
+struct tlv_p_entry {
+ uint16_t len;
+ const uint8_t *val;
+};
+
+enum tlv_type {
+ TLV_TYPE_NONE,
+ TLV_TYPE_FIXED,
+ TLV_TYPE_T,
+ TLV_TYPE_TV,
+ TLV_TYPE_TLV,
+ TLV_TYPE_TL16V,
+ TLV_TYPE_TvLV,
+ TLV_TYPE_SINGLE_TV
+};
+
+struct tlv_def {
+ enum tlv_type type;
+ uint8_t fixed_len;
+};
+
+struct tlv_definition {
+ struct tlv_def def[0xff];
+};
+
+struct tlv_parsed {
+ struct tlv_p_entry lv[0xff];
+};
+
+extern struct tlv_definition tvlv_att_def;
+
+int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
+ const struct tlv_definition *def,
+ const uint8_t *buf, int buf_len);
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
+ const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);
+/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
+void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
+
+#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
+#define TLVP_LEN(x, y) (x)->lv[y].len
+#define TLVP_VAL(x, y) (x)->lv[y].val
+
+#endif /* _TLV_H */
diff --git a/src/shared/libosmocore/include/osmocore/utils.h b/src/shared/libosmocore/include/osmocore/utils.h
new file mode 100644
index 00000000..51c6f035
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/utils.h
@@ -0,0 +1,20 @@
+#ifndef OSMOCORE_UTIL_H
+#define OSMOCORE_UTIL_H
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#include <stdint.h>
+
+struct value_string {
+ unsigned int value;
+ const char *str;
+};
+
+const char *get_value_string(const struct value_string *vs, uint32_t val);
+int get_string_value(const struct value_string *vs, const char *str);
+
+char bcd2char(uint8_t bcd);
+/* only works for numbers in ascci */
+uint8_t char2bcd(char c);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/write_queue.h b/src/shared/libosmocore/include/osmocore/write_queue.h
new file mode 100644
index 00000000..ef244c32
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/write_queue.h
@@ -0,0 +1,46 @@
+/* Generic write queue implementation */
+/*
+ * (C) 2010 by Holger Hans Peter Freyther
+ * (C) 2010 by On-Waves
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ *
+ */
+#ifndef write_queue_h
+#define write_queue_h
+
+#include "select.h"
+#include "msgb.h"
+
+struct write_queue {
+ struct bsc_fd bfd;
+ unsigned int max_length;
+ unsigned int current_length;
+
+ struct llist_head msg_queue;
+
+ int (*read_cb)(struct bsc_fd *fd);
+ int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
+ int (*except_cb)(struct bsc_fd *fd);
+};
+
+void write_queue_init(struct write_queue *queue, int max_length);
+void write_queue_clear(struct write_queue *queue);
+int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
+int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
+
+#endif
diff --git a/src/shared/libosmocore/libosmocore.pc.in b/src/shared/libosmocore/libosmocore.pc.in
new file mode 100644
index 00000000..7c298693
--- /dev/null
+++ b/src/shared/libosmocore/libosmocore.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom Core Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} -losmocore
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in
new file mode 100644
index 00000000..2cc0b5f8
--- /dev/null
+++ b/src/shared/libosmocore/libosmovty.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom VTY Interface Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} -losmovty
+Cflags: -I${includedir}/
+
diff --git a/src/shared/libosmocore/m4/DUMMY b/src/shared/libosmocore/m4/DUMMY
new file mode 100644
index 00000000..fda557ad
--- /dev/null
+++ b/src/shared/libosmocore/m4/DUMMY
@@ -0,0 +1 @@
+Dummply placeholder.
diff --git a/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am
index a7d450b1..a7d450b1 100644
--- a/src/Makefile.am
+++ b/src/shared/libosmocore/src/Makefile.am
diff --git a/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c
index 04c465a8..04c465a8 100644
--- a/src/bitvec.c
+++ b/src/shared/libosmocore/src/bitvec.c
diff --git a/src/comp128.c b/src/shared/libosmocore/src/comp128.c
index 5d5680c7..5d5680c7 100644
--- a/src/comp128.c
+++ b/src/shared/libosmocore/src/comp128.c
diff --git a/src/gprs_cipher_core.c b/src/shared/libosmocore/src/gprs_cipher_core.c
index 6174bd72..6174bd72 100644
--- a/src/gprs_cipher_core.c
+++ b/src/shared/libosmocore/src/gprs_cipher_core.c
diff --git a/src/gsm0808.c b/src/shared/libosmocore/src/gsm0808.c
index 1dc035b3..1dc035b3 100644
--- a/src/gsm0808.c
+++ b/src/shared/libosmocore/src/gsm0808.c
diff --git a/src/gsm48.c b/src/shared/libosmocore/src/gsm48.c
index daec4f39..daec4f39 100644
--- a/src/gsm48.c
+++ b/src/shared/libosmocore/src/gsm48.c
diff --git a/src/gsm48_ie.c b/src/shared/libosmocore/src/gsm48_ie.c
index 3c2a1f7b..3c2a1f7b 100644
--- a/src/gsm48_ie.c
+++ b/src/shared/libosmocore/src/gsm48_ie.c
diff --git a/src/gsm_utils.c b/src/shared/libosmocore/src/gsm_utils.c
index dc97ceff..dc97ceff 100644
--- a/src/gsm_utils.c
+++ b/src/shared/libosmocore/src/gsm_utils.c
diff --git a/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c
index abee4dac..abee4dac 100644
--- a/src/gsmtap_util.c
+++ b/src/shared/libosmocore/src/gsmtap_util.c
diff --git a/src/logging.c b/src/shared/libosmocore/src/logging.c
index 1dc30db3..1dc30db3 100644
--- a/src/logging.c
+++ b/src/shared/libosmocore/src/logging.c
diff --git a/src/msgb.c b/src/shared/libosmocore/src/msgb.c
index a60e2ffa..a60e2ffa 100644
--- a/src/msgb.c
+++ b/src/shared/libosmocore/src/msgb.c
diff --git a/src/plugin.c b/src/shared/libosmocore/src/plugin.c
index e953508a..e953508a 100644
--- a/src/plugin.c
+++ b/src/shared/libosmocore/src/plugin.c
diff --git a/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c
index f58b5c4a..f58b5c4a 100644
--- a/src/rate_ctr.c
+++ b/src/shared/libosmocore/src/rate_ctr.c
diff --git a/src/rsl.c b/src/shared/libosmocore/src/rsl.c
index c002d33e..c002d33e 100644
--- a/src/rsl.c
+++ b/src/shared/libosmocore/src/rsl.c
diff --git a/src/rxlev_stat.c b/src/shared/libosmocore/src/rxlev_stat.c
index 1bfd6795..1bfd6795 100644
--- a/src/rxlev_stat.c
+++ b/src/shared/libosmocore/src/rxlev_stat.c
diff --git a/src/select.c b/src/shared/libosmocore/src/select.c
index 2f6afa7f..2f6afa7f 100644
--- a/src/select.c
+++ b/src/shared/libosmocore/src/select.c
diff --git a/src/signal.c b/src/shared/libosmocore/src/signal.c
index c7ca86c4..c7ca86c4 100644
--- a/src/signal.c
+++ b/src/shared/libosmocore/src/signal.c
diff --git a/src/statistics.c b/src/shared/libosmocore/src/statistics.c
index 34e6a408..34e6a408 100644
--- a/src/statistics.c
+++ b/src/shared/libosmocore/src/statistics.c
diff --git a/src/talloc.c b/src/shared/libosmocore/src/talloc.c
index 98c2ee09..98c2ee09 100644
--- a/src/talloc.c
+++ b/src/shared/libosmocore/src/talloc.c
diff --git a/src/timer.c b/src/shared/libosmocore/src/timer.c
index 37d7d166..37d7d166 100644
--- a/src/timer.c
+++ b/src/shared/libosmocore/src/timer.c
diff --git a/src/tlv_parser.c b/src/shared/libosmocore/src/tlv_parser.c
index bbef7a9a..bbef7a9a 100644
--- a/src/tlv_parser.c
+++ b/src/shared/libosmocore/src/tlv_parser.c
diff --git a/src/utils.c b/src/shared/libosmocore/src/utils.c
index 4dab0645..4dab0645 100644
--- a/src/utils.c
+++ b/src/shared/libosmocore/src/utils.c
diff --git a/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am
index f2859cff..f2859cff 100644
--- a/src/vty/Makefile.am
+++ b/src/shared/libosmocore/src/vty/Makefile.am
diff --git a/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c
index a5655b93..a5655b93 100644
--- a/src/vty/buffer.c
+++ b/src/shared/libosmocore/src/vty/buffer.c
diff --git a/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c
index 21afa5c0..21afa5c0 100644
--- a/src/vty/command.c
+++ b/src/shared/libosmocore/src/vty/command.c
diff --git a/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c
index 896d79a9..896d79a9 100644
--- a/src/vty/logging_vty.c
+++ b/src/shared/libosmocore/src/vty/logging_vty.c
diff --git a/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c
index 90690960..90690960 100644
--- a/src/vty/telnet_interface.c
+++ b/src/shared/libosmocore/src/vty/telnet_interface.c
diff --git a/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c
index e163526e..e163526e 100644
--- a/src/vty/utils.c
+++ b/src/shared/libosmocore/src/vty/utils.c
diff --git a/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c
index 0343163f..0343163f 100644
--- a/src/vty/vector.c
+++ b/src/shared/libosmocore/src/vty/vector.c
diff --git a/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c
index ff17abf6..ff17abf6 100644
--- a/src/vty/vty.c
+++ b/src/shared/libosmocore/src/vty/vty.c
diff --git a/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c
index 618a8c0b..618a8c0b 100644
--- a/src/write_queue.c
+++ b/src/shared/libosmocore/src/write_queue.c
diff --git a/src/shared/libosmocore/tests/Makefile.am b/src/shared/libosmocore/tests/Makefile.am
new file mode 100644
index 00000000..0119a02c
--- /dev/null
+++ b/src/shared/libosmocore/tests/Makefile.am
@@ -0,0 +1,3 @@
+if ENABLE_TESTS
+SUBDIRS = timer sms
+endif
diff --git a/src/shared/libosmocore/tests/sms/Makefile.am b/src/shared/libosmocore/tests/sms/Makefile.am
new file mode 100644
index 00000000..a8f1ff6a
--- /dev/null
+++ b/src/shared/libosmocore/tests/sms/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = sms_test
+
+sms_test_SOURCES = sms_test.c
+sms_test_LDADD = $(top_builddir)/src/libosmocore.la
diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c
new file mode 100644
index 00000000..f5183d54
--- /dev/null
+++ b/src/shared/libosmocore/tests/sms/sms_test.c
@@ -0,0 +1,47 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <osmocore/msgb.h>
+#include <osmocore/gsm_utils.h>
+
+int main(int argc, char** argv)
+{
+ printf("SMS testing\n");
+ struct msgb *msg;
+ uint8_t *sms;
+ uint8_t i;
+
+ /* test 7-bit coding/decoding */
+ const char *input = "test text";
+ uint8_t length;
+ uint8_t coded[256];
+ char result[256];
+
+ length = gsm_7bit_encode(coded, input);
+ gsm_7bit_decode(result, coded, length);
+ if (strcmp(result, input) != 0) {
+ printf("7 Bit coding failed... life sucks\n");
+ printf("Wanted: '%s' got '%s'\n", input, result);
+ }
+}
diff --git a/src/shared/libosmocore/tests/timer/Makefile.am b/src/shared/libosmocore/tests/timer/Makefile.am
new file mode 100644
index 00000000..d3decf55
--- /dev/null
+++ b/src/shared/libosmocore/tests/timer/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = timer_test
+
+timer_test_SOURCES = timer_test.c
+timer_test_LDADD = $(top_builddir)/src/libosmocore.la
+
diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c
new file mode 100644
index 00000000..1b458d81
--- /dev/null
+++ b/src/shared/libosmocore/tests/timer/timer_test.c
@@ -0,0 +1,77 @@
+/*
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h>
+
+#include <osmocore/timer.h>
+#include <osmocore/select.h>
+
+#include "../../config.h"
+
+static void timer_fired(void *data);
+
+static struct timer_list timer_one = {
+ .cb = timer_fired,
+ .data = (void*)1,
+};
+
+static struct timer_list timer_two = {
+ .cb = timer_fired,
+ .data = (void*)2,
+};
+
+static struct timer_list timer_three = {
+ .cb = timer_fired,
+ .data = (void*)3,
+};
+
+static void timer_fired(void *_data)
+{
+ unsigned long data = (unsigned long) _data;
+ printf("Fired timer: %lu\n", data);
+
+ if (data == 1) {
+ bsc_schedule_timer(&timer_one, 3, 0);
+ bsc_del_timer(&timer_two);
+ } else if (data == 2) {
+ printf("Should not be fired... bug in del_timer\n");
+ } else if (data == 3) {
+ printf("Timer fired not registering again\n");
+ } else {
+ printf("wtf... wrong data\n");
+ }
+}
+
+int main(int argc, char** argv)
+{
+ printf("Starting... timer\n");
+
+ bsc_schedule_timer(&timer_one, 3, 0);
+ bsc_schedule_timer(&timer_two, 5, 0);
+ bsc_schedule_timer(&timer_three, 4, 0);
+
+#ifdef HAVE_SYS_SELECT_H
+ while (1) {
+ bsc_select_main(0);
+ }
+#else
+ printf("Select not supported on this platform!\n");
+#endif
+}
diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh
new file mode 100755
index 00000000..e7940c3d
--- /dev/null
+++ b/src/shared/update-libosmocore.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+(cd ../.. && git-subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master)
diff --git a/src/target/firmware/.gitignore b/src/target/firmware/.gitignore
new file mode 100644
index 00000000..79e98dfc
--- /dev/null
+++ b/src/target/firmware/.gitignore
@@ -0,0 +1,9 @@
+*.o
+*.p
+*.a
+*.lst
+*.bin
+*.elf
+*.map
+*.size
+*~
diff --git a/src/target/firmware/COPYING b/src/target/firmware/COPYING
new file mode 100644
index 00000000..d511905c
--- /dev/null
+++ b/src/target/firmware/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile
new file mode 100644
index 00000000..ca0a13c1
--- /dev/null
+++ b/src/target/firmware/Makefile
@@ -0,0 +1,41 @@
+
+# Global include path
+INCLUDES=-Iinclude/ -I../../../include -I../../shared/libosmocore/include
+
+# Various objects that are currently linked into all applications
+FLASH_OBJS=flash/cfi_flash.o
+DISPLAY_OBJS=display/font_r8x8.o display/font_r8x8_horiz.o display/st7558.o display/ssd1783.o display/display.o
+ABB_OBJS=abb/twl3025.o
+RF_OBJS=rf/trf6151.o
+
+# Board- and environment-specific startup code and linker script
+START=board/common/compal_ramload_start.S
+
+# List of all supported boards
+BOARDS?=compal_e88 compal_e99 gta0x
+compal_COMMON_OBJS=board/common/rffe_compal_dualband.o board/common/calypso_uart.o board/common/calypso_pwl.o
+gta0x_COMMON_OBJS=board/common/rffe_gta0x_triband.o board/common/calypso_uart.o board/common/calypso_pwl.o
+compal_e88_OBJS=$(compal_COMMON_OBJS) board/compal_e88/init.o
+compal_e99_OBJS=$(compal_COMMON_OBJS) board/compal_e99/init.o
+gta0x_OBJS=$(gta0x_COMMON_OBJS) board/gta0x/init.o
+
+# List of all supported execution environments
+ENVIRONMENTS?=ramload osmoload
+ramload_LDS=board/common/compal_ramload.lds
+osmoload_LDS=board/common/compal_osmoload.lds
+
+# List of all applications (add yours here!)
+APPLICATIONS=hello_world compal_dump compal_dsp_dump layer1 loader
+
+# Things that go in all applications
+ANY_APP_OBJS+=$(START:.S=.o) $(ABB_OBJS) $(RF_OBJS) $(DISPLAY_OBJS) $(FLASH_OBJS)
+ANY_APP_LIBS+=calypso/libcalypso.a layer1/liblayer1.a lib/libmini.a comm/libcomm.a ../../shared/libosmocore/build-target/src/.libs/libosmocore.a
+
+# Libraries are defined in subdirectories
+-include calypso/Makefile
+-include layer1/Makefile
+-include comm/Makefile
+-include lib/Makefile
+
+# Include rules
+-include Makefile.inc
diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc
new file mode 100644
index 00000000..7f91fb18
--- /dev/null
+++ b/src/target/firmware/Makefile.inc
@@ -0,0 +1,183 @@
+
+#### TOOLCHAIN CONFIGURATION ####
+
+CROSS_COMPILE?=arm-elf-
+
+CC=gcc
+LD=ld
+AR=ar
+SIZE=size
+OBJCOPY=objcopy
+
+DEBUGF=dwarf-2
+
+CFLAGS=-mcpu=arm7tdmi $(INCLUDES)
+CFLAGS += -Wall -Wextra -Wcast-align -Wimplicit -Wunused
+CFLAGS += -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wnested-externs
+CFLAGS += -Wbad-function-cast -Wsign-compare -Waggregate-return
+CFLAGS += -Wa,-adhlns=$(subst $(suffix $<),.lst,$<)
+CFLAGS += -Os -ffunction-sections
+CFLAGS += -g$(DEBUGF)
+
+# Uncomment this line if you want to enable Tx (Transmit) Support.
+#CFLAGS += -DCONFIG_TX_ENABLE
+
+# some older toolchains don't support this, ignore it for now
+#ASFLAGS=-Wa,-adhlns=$(<:.S=.lst),--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__
+ASFLAGS=-Wa,-adhlns=$(<:.S=.lst) $(INCLUDES) -D__ASSEMBLY__
+
+LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections #-Wl,-Map=$(TARGET).map,--cref
+
+#### GIT VERSION ####
+
+GIT_COMMIT:=$(shell git describe --always)
+GIT_MODIFIED:=$(shell (git status | grep "modified:\|added:\|deleted:" -q) && echo "-modified")
+
+GIT_REVISION:=$(GIT_COMMIT)$(GIT_MODIFIED)
+
+CFLAGS += -DGIT_REVISION=\"$(GIT_REVISION)\"
+
+#### GLOBAL DATA ####
+
+ALL_OBJS=
+ALL_DEPS=
+
+ALL_LSTS=$(ALL_OBJS:.o=.lst)
+
+#### APPLICATION DATA ####
+
+ALL_APPS=
+
+ALL_APP_TARGETS=$(ALL_APPS) $(ALL_APPS:.elf=.bin) $(ALL_APPS:.elf=.map) $(ALL_APPS:.elf=.size)
+
+#### LIBRARY DATA ####
+
+ALL_LIBS=
+
+ALL_LIB_TARGETS=$(ALL_LIBS)
+
+
+#### DEFAULT RULE ####
+
+.PHONY: default
+default: all
+
+
+#### APPLICATION RULES ####
+
+ALL_OBJS+=$(ANY_APP_OBJS)
+ALL_DEPS+=$(ANY_APP_OBJS:.o=.p)
+
+# template for application rules
+define APPLICATION_BOARD_ENVIRONMENT_template
+
+# define set of objects for this binary
+$(1)_$(2)_$(3)_OBJS := apps/$(1)/main.o $(ANY_APP_OBJS) $(ANY_APP_LIBS) $$($(2)_OBJS)
+
+# define manifest compilation
+board/$(2)/$(1).$(3).manifest.o: board/manifest.c
+ $(CROSS_COMPILE)$(CC) $(CFLAGS) -DAPPLICATION=\"$(3)\" -DBOARD=\"$(2)\" -DENVIRONMENT=\"$(3)\" -c -o $$@ $$<
+
+# add manifest object to object list
+$(1)_$(2)_$(3)_OBJS+=board/$(2)/$(1).$(3).manifest.o
+
+# define compilation, generating various extra files on the way
+board/$(2)/$(1).$(3).elf board/$(2)/$(1).$(3).map board/$(2)/$(1).$(3).size: $$($(1)_$(2)_$(3)_OBJS) $$($(3)_LDS)
+ $(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $$($(3)_LDS) -Bstatic \
+ -Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \
+ --start-group $$($(1)_$(2)_$(3)_OBJS) --end-group
+ $(CROSS_COMPILE)$(SIZE) board/$(2)/$(1).$(3).elf | tee board/$(2)/$(1).$(3).size
+
+ALL_APPS+=board/$(2)/$(1).$(3).elf
+
+endef
+
+define BOARD_template
+
+ALL_OBJS+=$$($(1)_OBJS)
+
+endef
+
+define APPLICATION_template
+
+$(1)_SRCS_REL=$$(patsubst %,$$($(1)_DIR)/%,$$($(1)_SRCS))
+$(1)_OBJS:=$$($(1)_SRCS_REL:.c=.o)
+$(1)_OBJS:=$$($(1)_OBJS:.S=.o)
+
+ALL_OBJS+=$$($(1)_OBJS) apps/$(1)/main.o
+ALL_DEPS+=$$($(1)_OBJS:.o=.p) apps/$(1)/main.p
+
+endef
+
+# define rules for all defined applications
+$(foreach app,$(APPLICATIONS),$(foreach brd,$(BOARDS),$(foreach env,$(ENVIRONMENTS),$(eval $(call APPLICATION_BOARD_ENVIRONMENT_template,$(app),$(brd),$(env))))))
+$(foreach brd,$(BOARDS),$(eval $(call BOARD_template,$(brd))))
+$(foreach app,$(APPLICATIONS),$(eval $(call APPLICATION_template,$(app))))
+
+
+#### LIBRARY RULES ####
+
+# template for library rules
+define LIBRARY_template
+
+$(1)_SRCS_REL=$$(patsubst %,$$($(1)_DIR)/%,$$($(1)_SRCS))
+$(1)_OBJS:=$$($(1)_SRCS_REL:.c=.o)
+$(1)_OBJS:=$$($(1)_OBJS:.S=.o)
+
+$$($(1)_DIR)/lib$(1).a: $$($(1)_OBJS)
+ $(CROSS_COMPILE)$(AR) cru $$($(1)_DIR)/lib$(1).a $$($(1)_OBJS)
+
+ALL_LIBS+=$$($(1)_DIR)/lib$(1).a
+
+ALL_OBJS+=$$($(1)_OBJS)
+ALL_DEPS+=$$($(1)_OBJS:.o=.p)
+
+endef
+
+# define rules for all defined libraries
+$(foreach lbr,$(LIBRARIES),$(eval $(call LIBRARY_template,$(lbr))))
+
+
+#### TOPLEVEL RULES ####
+
+.PHONY: all
+all: $(ALL_DEPS) $(ALL_APP_TARGETS) $(ALL_LIB_TARGETS)
+
+.PHONY: depend
+depend: $(ALL_DEPS)
+
+
+#### COMPILATION RULES ####
+
+%.p: %.c
+ @$(CROSS_COMPILE)$(CC) $(CFLAGS) -M -o $(*).d $(<)
+ @sed 's|.*\.o:|$(@:.p=.o): |g' < $*.d > $@; rm -f $*.d; [ -s $@ ] || rm -f $@
+
+%.p: %.S
+ @$(CROSS_COMPILE)$(CC) $(ASFLAGS) -M -o $(*).d $(<)
+ @sed 's|.*\.o:|$(@:.p=.o): |g' < $*.d > $@; rm -f $*.d; [ -s $@ ] || rm -f $@
+
+%.o: %.c
+ $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $<
+
+%.o: %.S
+ $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $<
+
+
+%.bin: %.elf
+ $(CROSS_COMPILE)objcopy --gap-fill=0xff -O binary $^ $@
+
+
+#### CLEANUP RULES ####
+
+.PHONY: clean
+clean:
+ rm -f $(ALL_APP_TARGETS) $(ALL_LIB_TARGETS) $(ALL_OBJS) $(ALL_DEPS) $(ALL_LSTS)
+
+.PHONY: distclean
+distclean: clean
+
+
+#### DEPENDENCY LOAD ####
+
+-include $(ALL_DEPS)
diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c
new file mode 100644
index 00000000..01817130
--- /dev/null
+++ b/src/target/firmware/abb/twl3025.c
@@ -0,0 +1,356 @@
+/* Driver for Analog Baseband Circuit (TWL3025) */
+
+/* (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 <delay.h>
+#include <memory.h>
+#include <spi.h>
+#include <calypso/irq.h>
+#include <calypso/tsp.h>
+#include <calypso/tpu.h>
+#include <abb/twl3025.h>
+
+/* TWL3025 */
+#define REG_PAGE(n) (n >> 7)
+#define REG_ADDR(n) (n & 0x3f)
+
+#define TWL3025_DEV_IDX 0 /* On the SPI bus */
+#define TWL3025_TSP_DEV_IDX 0 /* On the TSP bus */
+
+/* values encountered on a GTA-02 for GSM900 (the same for GSM1800!?) */
+const uint16_t twl3025_default_ramp[16] = {
+ ABB_RAMP_VAL( 0, 0),
+ ABB_RAMP_VAL( 0, 11),
+ ABB_RAMP_VAL( 0, 31),
+ ABB_RAMP_VAL( 0, 31),
+ ABB_RAMP_VAL( 0, 31),
+ ABB_RAMP_VAL( 0, 24),
+ ABB_RAMP_VAL( 0, 0),
+ ABB_RAMP_VAL( 0, 0),
+ ABB_RAMP_VAL( 9, 0),
+ ABB_RAMP_VAL(18, 0),
+ ABB_RAMP_VAL(25, 0),
+ ABB_RAMP_VAL(31, 0),
+ ABB_RAMP_VAL(30, 0),
+ ABB_RAMP_VAL(15, 0),
+ ABB_RAMP_VAL( 0, 0),
+ ABB_RAMP_VAL( 0, 0),
+};
+
+struct twl3025 {
+ uint8_t page;
+};
+static struct twl3025 twl3025_state;
+
+/* Switch the register page of the TWL3025 */
+static void twl3025_switch_page(uint8_t page)
+{
+ if (page == 0)
+ twl3025_reg_write(PAGEREG, 1 << 0);
+ else
+ twl3025_reg_write(PAGEREG, 1 << 1);
+
+ twl3025_state.page = page;
+}
+
+static void handle_charger(void)
+{
+ uint16_t status;
+ printd("handle_charger();");
+
+ status = twl3025_reg_read(VRPCSTS);
+// printd("\nvrpcsts: 0x%02x", status);
+
+ if (status & 0x40) {
+ printd(" inserted\n");
+ } else {
+ printd(" removed\n");
+ }
+
+// twl3025_dump_madc();
+}
+
+static void handle_adc_done(void)
+{
+ printd("handle_adc_done();");
+}
+
+static void twl3025_irq(enum irq_nr nr)
+{
+ uint16_t src;
+ printd("twl3025_irq: 0x%02x\n",nr);
+ switch (nr){
+ case IRQ_EXTERNAL: // charger in/out, pwrbtn, adc done
+ src = twl3025_reg_read(ITSTATREG);
+// printd("itstatreg 0x%02x\n", src);
+ if (src & 0x08)
+ handle_charger();
+ if (src & 0x20)
+ handle_adc_done();
+ break;
+ case IRQ_EXTERNAL_FIQ: // vcc <2.8V emergency power off
+ puts("\nBROWNOUT!1!");
+ twl3025_power_off();
+ break;
+ default:
+ return;
+ }
+}
+
+void twl3025_init(void)
+{
+ spi_init();
+ twl3025_switch_page(0);
+ twl3025_clk13m(1);
+ twl3025_reg_write(AFCCTLADD, 0x01); /* AFCCK(1:0) must not be zero! */
+ twl3025_unit_enable(TWL3025_UNIT_AFC, 1);
+
+ irq_register_handler(IRQ_EXTERNAL, &twl3025_irq);
+ irq_config(IRQ_EXTERNAL, 0, 0, 0);
+ irq_enable(IRQ_EXTERNAL);
+
+ irq_register_handler(IRQ_EXTERNAL_FIQ, &twl3025_irq);
+ irq_config(IRQ_EXTERNAL_FIQ, 1, 0, 0);
+ irq_enable(IRQ_EXTERNAL_FIQ);
+}
+
+void twl3025_reg_write(uint8_t reg, uint16_t data)
+{
+ uint16_t tx;
+
+ printd("tw3025_reg_write(%u,%u)=0x%04x\n", REG_PAGE(reg),
+ REG_ADDR(reg), data);
+
+ if (reg != PAGEREG && REG_PAGE(reg) != twl3025_state.page)
+ twl3025_switch_page(REG_PAGE(reg));
+
+ tx = ((data & 0x3ff) << 6) | (REG_ADDR(reg) << 1);
+
+ spi_xfer(TWL3025_DEV_IDX, 16, &tx, NULL);
+}
+
+void twl3025_tsp_write(uint8_t data)
+{
+ tsp_write(TWL3025_TSP_DEV_IDX, 7, data);
+}
+
+uint16_t twl3025_reg_read(uint8_t reg)
+{
+ uint16_t tx, rx;
+
+ if (REG_PAGE(reg) != twl3025_state.page)
+ twl3025_switch_page(REG_PAGE(reg));
+
+ tx = (REG_ADDR(reg) << 1) | 1;
+
+ /* A read cycle contains two SPI transfers */
+ spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx);
+ delay_ms(1);
+ spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx);
+
+ rx >>= 6;
+
+ printd("tw3025_reg_read(%u,%u)=0x%04x\n", REG_PAGE(reg),
+ REG_ADDR(reg), rx);
+
+ return rx;
+}
+
+static void twl3025_wait_ibic_access(void)
+{
+ /* Wait 6 * 32kHz clock cycles for first IBIC access (187us + 10% = 210us) */
+ delay_ms(1);
+}
+
+void twl3025_power_off(void)
+{
+ twl3025_reg_write(VRPCDEV, 0x01);
+}
+
+void twl3025_clk13m(int enable)
+{
+ if (enable) {
+ twl3025_reg_write(TOGBR2, TOGBR2_ACTS);
+ twl3025_wait_ibic_access();
+ /* for whatever reason we need to do this twice */
+ twl3025_reg_write(TOGBR2, TOGBR2_ACTS);
+ twl3025_wait_ibic_access();
+ } else {
+ twl3025_reg_write(TOGBR2, TOGBR2_ACTR);
+ twl3025_wait_ibic_access();
+ }
+}
+
+#define TSP_DELAY 6 /* 13* Tclk6M5 = ~ 3 GSM Qbits + 3 TPU instructions */
+#define BDLON_TO_BDLCAL 6
+#define BDLCAL_DURATION 66
+#define BDLON_TO_BDLENA 7
+#define BULON_TO_BULENA 16
+#define BULON_TO_BULCAL 17
+#define BULCAL_DURATION 143 /* really that long? */
+
+/* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL - TSP_DELAY */
+#define DOWNLINK_DELAY (3 * TSP_DELAY + BDLCAL_DURATION + BDLON_TO_BDLCAL)
+
+/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */
+void twl3025_downlink(int on, int16_t at)
+{
+ int16_t bdl_ena = at - TSP_DELAY - 6;
+
+ if (on) {
+ if (bdl_ena < 0)
+ printf("BDLENA time negative (%d)\n", bdl_ena);
+ /* calibration should be done just before BDLENA */
+ tpu_enq_at(bdl_ena - DOWNLINK_DELAY);
+ /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL - TSP_DELAY */
+ twl3025_tsp_write(BDLON);
+ /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL */
+ tpu_enq_wait(BDLON_TO_BDLCAL - TSP_DELAY);
+ /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY */
+ twl3025_tsp_write(BDLON | BDLCAL);
+ /* bdl_ena - TSP_DELAY - BDLCAL_DURATION */
+ tpu_enq_wait(BDLCAL_DURATION - TSP_DELAY);
+ /* bdl_ena - TSP_DELAY */
+ twl3025_tsp_write(BDLON);
+ //tpu_enq_wait(BDLCAL_TO_BDLENA) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY
+ tpu_enq_at(bdl_ena);
+ twl3025_tsp_write(BDLON | BDLENA);
+ } else {
+ tpu_enq_at(bdl_ena);
+ twl3025_tsp_write(BDLON);
+ //tpu_enq_wait(nBDLENA_TO_nBDLON) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY
+ twl3025_tsp_write(0);
+ }
+}
+
+/* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL - TSP_DELAY */
+#define UPLINK_DELAY (3 * TSP_DELAY + BULCAL_DURATION + BULON_TO_BULCAL + 35)
+
+void twl3025_uplink(int on, int16_t at)
+{
+ int16_t bul_ena = at - TSP_DELAY - 6;
+
+ if (bul_ena < 0)
+ printf("BULENA time negative (%d)\n", bul_ena);
+ if (on) {
+ /* calibration should be done just before BULENA */
+ tpu_enq_at(bul_ena - UPLINK_DELAY);
+ /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL - TSP_DELAY */
+ twl3025_tsp_write(BULON);
+ /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL */
+ tpu_enq_wait(BULON_TO_BULCAL - TSP_DELAY);
+ /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY */
+ twl3025_tsp_write(BULON | BULCAL);
+ /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION */
+ tpu_enq_wait(BULCAL_DURATION - TSP_DELAY);
+ /* bdl_ena - 35 - TSP_DELAY */
+ twl3025_tsp_write(BULON);
+ /* bdl_ena - 35 */
+ tpu_enq_wait(35); /* minimum time required to bring the ramp up (really needed?) */
+ tpu_enq_at(bul_ena);
+ twl3025_tsp_write(BULON | BULENA);
+ } else {
+ tpu_enq_at(bul_ena);
+ twl3025_tsp_write(BULON);
+ tpu_enq_wait(35); /* minimum time required to bring the ramp down (needed!) */
+ twl3025_tsp_write(0);
+ }
+}
+
+void twl3025_afc_set(int16_t val)
+{
+ printf("twl3025_afc_set(%d)\n", val);
+
+ if (val > 4095)
+ val = 4095;
+ else if (val <= -4096)
+ val = -4096;
+
+ /* FIXME: we currently write from the USP rather than BSP */
+ twl3025_reg_write(AUXAFC2, val >> 10);
+ twl3025_reg_write(AUXAFC1, val & 0x3ff);
+}
+
+int16_t twl3025_afc_get(void)
+{
+ int16_t val;
+
+ val = (twl3025_reg_read(AUXAFC2) & 0x7);
+ val = val << 10;
+ val = val | (twl3025_reg_read(AUXAFC1) & 0x3ff);
+
+ if (val > 4095)
+ val = -(8192 - val);
+ return val;
+}
+
+void twl3025_unit_enable(enum twl3025_unit unit, int on)
+{
+ uint16_t togbr1 = 0;
+
+ switch (unit) {
+ case TWL3025_UNIT_AFC:
+ if (on)
+ togbr1 = (1 << 7);
+ else
+ togbr1 = (1 << 6);
+ break;
+ case TWL3025_UNIT_MAD:
+ if (on)
+ togbr1 = (1 << 9);
+ else
+ togbr1 = (1 << 8);
+ break;
+ case TWL3025_UNIT_ADA:
+ if (on)
+ togbr1 = (1 << 5);
+ else
+ togbr1 = (1 << 4);
+ case TWL3025_UNIT_VDL:
+ if (on)
+ togbr1 = (1 << 3);
+ else
+ togbr1 = (1 << 2);
+ break;
+ case TWL3025_UNIT_VUL:
+ if (on)
+ togbr1 = (1 << 1);
+ else
+ togbr1 = (1 << 0);
+ break;
+ }
+ twl3025_reg_write(TOGBR1, togbr1);
+}
+
+uint8_t twl3025_afcout_get(void)
+{
+ return twl3025_reg_read(AFCOUT) & 0xff;
+}
+
+void twl3025_afcout_set(uint8_t val)
+{
+ twl3025_reg_write(AFCCTLADD, 0x05);
+ twl3025_reg_write(AFCOUT, val);
+}
diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c
new file mode 100644
index 00000000..282bce28
--- /dev/null
+++ b/src/target/firmware/apps/compal_dsp_dump/main.c
@@ -0,0 +1,74 @@
+/* main program of Free Software for Calypso Phone */
+
+/* (C) 2010 Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 Sylvain Munaut <tnt@246tNt.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 <memory.h>
+#include <stdio.h>
+#include <stdint.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 <comm/timer.h>
+
+/* FIXME: We need proper calibrated delay loops at some point! */
+void delay_us(unsigned int us)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < us*4; i++) { i; }
+}
+
+void delay_ms(unsigned int ms)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < ms*1300; i++) { i; }
+}
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+int main(void)
+{
+ puts("\n\nCompal DSP data dumper\n");
+ puts(hr);
+
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ /* Dump DSP content */
+ dsp_dump();
+
+ while (1) {
+ update_timers();
+ }
+}
+
diff --git a/src/target/firmware/apps/compal_dump/main.c b/src/target/firmware/apps/compal_dump/main.c
new file mode 100644
index 00000000..0eaf9b79
--- /dev/null
+++ b/src/target/firmware/apps/compal_dump/main.c
@@ -0,0 +1,93 @@
+/* main program of Free Software for Calypso Phone */
+
+/* (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 <memory.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <cfi_flash.h>
+#include <abb/twl3025.h>
+#include <calypso/clock.h>
+#include <calypso/timer.h>
+#include <calypso/misc.h>
+#include <comm/timer.h>
+
+/* FIXME: We need proper calibrated delay loops at some point! */
+void delay_us(unsigned int us)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < us*4; i++) { i; }
+}
+
+void delay_ms(unsigned int ms)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < ms*1300; i++) { i; }
+}
+
+#define KBIT 1024
+#define MBIT (1024*KBIT)
+
+#define REG_DEV_ID_CODE 0xfffef000
+#define REG_DEV_VER_CODE 0xfffef002
+#define REG_DEV_ARMVER_CODE 0xfffffe00
+#define REG_cDSP_ID_CODE 0xfffffe02
+#define REG_DIE_ID_CODE 0xfffef010
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+int main(void)
+{
+ puts("\n\nCompal device data dumper\n");
+ puts(hr);
+
+ /* Disable watchdog (for phones that have it enabled after boot) */
+ wdog_enable(0);
+
+ /* Initialize TWL3025 for power control */
+ twl3025_init();
+
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ /* Initialize flash, dumping the protection area. */
+ cfi_flash_t f;
+ flash_init(&f, 0x00000000);
+ flash_dump_info(&f);
+ puts(hr);
+
+ /* Dump flash contents */
+ printf("Dump %lu kbytes of external flash\n", f.f_size/1024);
+ memdump_range((void *)0x00000000, f.f_size);
+ puts(hr);
+
+ /* Power down */
+ twl3025_power_off();
+
+ while (1) {
+ update_timers();
+ }
+}
+
diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c
new file mode 100644
index 00000000..effbc90a
--- /dev/null
+++ b/src/target/firmware/apps/hello_world/main.c
@@ -0,0 +1,137 @@
+/* main program of Free Software for Calypso Phone */
+
+/* (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 <string.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+#include <abb/twl3025.h>
+#include <display.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 <comm/sercomm.h>
+#include <comm/timer.h>
+
+/* FIXME: We need proper calibrated delay loops at some point! */
+void delay_us(unsigned int us)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < us*4; i++) { i; }
+}
+
+void delay_ms(unsigned int ms)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < ms*1300; i++) { i; }
+}
+
+/* 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);
+ display_puts((char *) 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)
+{
+ puts("\n\nHello World from " __FILE__ " program code\n");
+ puts(hr);
+ /* 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 aftee PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ /* Dump all memory */
+ //dump_mem();
+#if 0
+ /* Dump Bootloader */
+ memdump_range((void *)0x00000000, 0x2000);
+ puts(hr);
+#endif
+
+ display_set_attr(DISP_ATTR_INVERT);
+ display_puts("Hello World");
+
+ sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb);
+ sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
+
+ /* beyond this point we only react to interrupts */
+ puts("entering interrupt loop\n");
+ while (1) {
+ update_timers();
+ }
+
+ twl3025_power_off();
+
+ while (1) {}
+}
+
+void key_handler(enum key_codes code, enum key_states state)
+{
+ if (state != PRESSED)
+ return;
+
+ switch (code) {
+ default:
+ break;
+ }
+}
diff --git a/src/target/firmware/apps/l1test/main.c b/src/target/firmware/apps/l1test/main.c
new file mode 100644
index 00000000..6e78f9c9
--- /dev/null
+++ b/src/target/firmware/apps/l1test/main.c
@@ -0,0 +1,288 @@
+/* main program of Free Software for Calypso Phone */
+
+/* (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 <memory.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+
+#include <abb/twl3025.h>
+#include <display.h>
+#include <rf/trf6151.h>
+
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+
+#include <calypso/clock.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/irq.h>
+#include <calypso/misc.h>
+
+#include <layer1/sync.h>
+#include <layer1/tpu_window.h>
+
+#define SCAN
+
+#ifdef SCAN
+/* if scanning is enabled, scan from 0 ... 124 */
+#define BASE_ARFCN 0
+#else
+/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */
+#define BASE_ARFCN 871
+#endif
+
+/* FIXME: We need proper calibrated delay loops at some point! */
+void delay_us(unsigned int us)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < us*4; i++) { i; }
+}
+
+void delay_ms(unsigned int ms)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < ms*1300; i++) { i; }
+}
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+/* Best ARFCN MAP ************************************************************/
+
+struct arfcn_map {
+ uint16_t arfcn;
+ int16_t dbm8;
+};
+
+static struct arfcn_map best_arfcn_map[10];
+static void best_arfcn_update(uint16_t arfcn, int16_t dbm8)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) {
+ if (best_arfcn_map[i].dbm8 < dbm8 ||
+ best_arfcn_map[i].dbm8 == 0) {
+ best_arfcn_map[i].dbm8 = dbm8;
+ best_arfcn_map[i].arfcn = arfcn;
+ return;
+ }
+ }
+}
+
+static void best_arfcn_dump(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) {
+ if (best_arfcn_map[i].dbm8 == 0)
+ continue;
+ printf("ARFCN %3d: %d dBm\n",
+ best_arfcn_map[i].arfcn,
+ best_arfcn_map[i].dbm8/8);
+ }
+}
+
+
+/* MAIN program **************************************************************/
+
+enum l1test_state {
+ STATE_NONE,
+ STATE_PM,
+ STATE_FB,
+};
+
+static void l1test_state_change(enum l1test_state new_state)
+{
+ switch (new_state) {
+ case STATE_PM:
+ puts("Performing power measurement over GSM900\n");
+ l1s_pm_test(1, BASE_ARFCN);
+ break;
+ case STATE_FB:
+ puts("Starting FCCH Recognition\n");
+ l1s_fb_test(1, 0);
+ break;
+ case STATE_NONE:
+ /* disable frame interrupts */
+ tpu_frame_irq_en(0, 0);
+ break;
+ }
+}
+
+/* completion call-back for the L1 Sync Pwer Measurement */
+static void l1s_signal_cb(struct l1_signal *sig)
+{
+ uint16_t i, next_arfcn;
+
+ switch (sig->signum) {
+ case L1_SIG_PM:
+ best_arfcn_update(sig->arfcn, sig->pm.dbm8[0]);
+ next_arfcn = sig->arfcn + 1;
+
+ if (next_arfcn >= 124) {
+ puts("ARFCN Top 10 Rx Level\n");
+ best_arfcn_dump();
+
+ trf6151_rx_window(0, best_arfcn_map[0].arfcn, 40, 0);
+ tpu_end_scenario();
+
+ /* PM phase completed, do FB det */
+ l1test_state_change(STATE_FB);
+
+ break;
+ }
+
+ /* restart Power Measurement */
+ l1s_pm_test(1, next_arfcn);
+ break;
+ case L1_SIG_NB:
+ puts("NB SNR ");
+ for (i = 0; i < 4; i++) {
+ uint16_t snr = sig->nb.meas[i].snr;
+ printf("%d.%03u ", l1s_snr_int(snr), l1s_snr_fract(snr));
+ }
+ putchar('\n');
+ printf("--> Frame %d %d 0x%04X ", sig->nb.fire, sig->nb.crc, sig->nb.num_biterr);
+ for (i = 0; i < ARRAY_SIZE(sig->nb.frame); i++)
+ printf("%02X ", sig->nb.frame[i]);
+ putchar('\n');
+ break;
+ }
+}
+
+static void key_handler(enum key_codes code, enum key_states state);
+
+int main(void)
+{
+ puts("\n\nHello World from " __FILE__ " program code\n");
+
+ puts(hr);
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ keypad_set_handler(&key_handler);
+
+ /* Dump clock config aftee PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ display_set_attr(DISP_ATTR_INVERT);
+ display_puts("l1test.bin");
+
+ layer1_init();
+ l1s_set_handler(&l1s_signal_cb);
+
+ //dsp_checksum_task();
+#ifdef SCAN
+ l1test_state_change(STATE_PM);
+#else
+ l1test_state_change(STATE_FB);
+#endif
+ tpu_frame_irq_en(1, 1);
+
+ while (1) {
+ update_timers();
+ }
+
+ /* NOT REACHED */
+
+ twl3025_power_off();
+}
+
+static int8_t vga_gain = 40;
+static int high_gain = 0;
+static int afcout = 0;
+
+static void update_vga_gain(void)
+{
+ printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW");
+ trf6151_set_gain(vga_gain, high_gain);
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+}
+
+static void tspact_toggle(uint8_t num)
+{
+ printf("TSPACT%u toggle\n", num);
+ tsp_act_toggle((1 << num));
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+}
+
+static void key_handler(enum key_codes code, enum key_states state)
+{
+ if (state != PRESSED)
+ return;
+
+ switch (code) {
+ case KEY_1: /* VGA gain decrement */
+ vga_gain -= 2;
+ if (vga_gain < 14)
+ vga_gain = 14;
+ update_vga_gain();
+ break;
+ case KEY_2: /* High/Low Rx gain */
+ high_gain ^= 1;
+ update_vga_gain();
+ break;
+ case KEY_3: /* VGA gain increment */
+ vga_gain += 2;
+ if (vga_gain > 40)
+ vga_gain = 40;
+ update_vga_gain();
+ break;
+ case KEY_4:
+ tspact_toggle(6); /* TRENA (RFFE) */
+ break;
+ case KEY_5:
+ tspact_toggle(8); /* GSM_TXEN (RFFE) */
+ break;
+ case KEY_6:
+ tspact_toggle(1); /* PAENA (RFFE) */
+ break;
+ case KEY_7: /* decrement AFC OUT */
+ afcout -= 100;
+ if (afcout < -4096)
+ afcout = -4096;
+ twl3025_afc_set(afcout);
+ printf("AFC OUT: %u\n", twl3025_afcout_get());
+ break;
+ case KEY_9: /* increase AFC OUT */
+ afcout += 100;
+ if (afcout > 4095)
+ afcout = 4095;
+ twl3025_afc_set(afcout);
+ printf("AFC OUT: %u\n", twl3025_afcout_get());
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c
new file mode 100644
index 00000000..cf45d7fd
--- /dev/null
+++ b/src/target/firmware/apps/layer1/main.c
@@ -0,0 +1,173 @@
+/* main program of Free Software for Calypso Phone */
+
+/* (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 <memory.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+
+#include <abb/twl3025.h>
+#include <display.h>
+#include <rf/trf6151.h>
+
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+
+#include <calypso/clock.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/irq.h>
+#include <calypso/misc.h>
+
+#include <layer1/sync.h>
+#include <layer1/tpu_window.h>
+
+/* FIXME: We need proper calibrated delay loops at some point! */
+void delay_us(unsigned int us)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < us*4; i++) { i; }
+}
+
+void delay_ms(unsigned int ms)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < ms*1300; i++) { i; }
+}
+
+const char *hr = "======================================================================\n";
+
+/* MAIN program **************************************************************/
+
+static void key_handler(enum key_codes code, enum key_states state);
+
+int main(void)
+{
+ puts("\n\nHello World from " __FILE__ " program code\n");
+
+ puts(hr);
+ /* Dump device identification */
+ dump_dev_id();
+ puts(hr);
+
+ keypad_set_handler(&key_handler);
+
+ /* Dump clock config aftee PLL set */
+ calypso_clk_dump();
+ puts(hr);
+
+ display_set_attr(DISP_ATTR_INVERT);
+ display_puts("layer1.bin");
+
+ layer1_init();
+
+ tpu_frame_irq_en(1, 1);
+
+ while (1) {
+ l1a_compl_execute();
+ update_timers();
+ }
+
+ /* NOT REACHED */
+
+ twl3025_power_off();
+}
+
+static int8_t vga_gain = 40;
+static int high_gain = 0;
+static int afcout = 0;
+
+static void update_vga_gain(void)
+{
+ printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW");
+ trf6151_set_gain(vga_gain, high_gain);
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+}
+
+static void tspact_toggle(uint8_t num)
+{
+ printf("TSPACT%u toggle\n", num);
+ tsp_act_toggle((1 << num));
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+}
+
+static void key_handler(enum key_codes code, enum key_states state)
+{
+ if (state != PRESSED)
+ return;
+
+ switch (code) {
+ case KEY_1: /* VGA gain decrement */
+ vga_gain -= 2;
+ if (vga_gain < 14)
+ vga_gain = 14;
+ update_vga_gain();
+ break;
+ case KEY_2: /* High/Low Rx gain */
+ high_gain ^= 1;
+ update_vga_gain();
+ break;
+ case KEY_3: /* VGA gain increment */
+ vga_gain += 2;
+ if (vga_gain > 40)
+ vga_gain = 40;
+ update_vga_gain();
+ break;
+ case KEY_4:
+ tspact_toggle(6); /* TRENA (RFFE) */
+ break;
+ case KEY_5:
+ tspact_toggle(8); /* GSM_TXEN (RFFE) */
+ break;
+ case KEY_6:
+ tspact_toggle(1); /* PAENA (RFFE) */
+ break;
+ case KEY_7: /* decrement AFC OUT */
+ afcout -= 100;
+ if (afcout < -4096)
+ afcout = -4096;
+ twl3025_afc_set(afcout);
+ printf("AFC OUT: %u\n", twl3025_afcout_get());
+ break;
+ case KEY_9: /* increase AFC OUT */
+ afcout += 100;
+ if (afcout > 4095)
+ afcout = 4095;
+ twl3025_afc_set(afcout);
+ printf("AFC OUT: %u\n", twl3025_afcout_get());
+ break;
+ default:
+ break;
+ }
+}
+
+
diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c
new file mode 100644
index 00000000..f4e1f545
--- /dev/null
+++ b/src/target/firmware/apps/loader/main.c
@@ -0,0 +1,225 @@
+/* boot loader for Calypso phones */
+
+/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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 <string.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rffe.h>
+#include <keypad.h>
+#include <board.h>
+#include <console.h>
+
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+
+#include <comm/sercomm.h>
+
+#include <calypso/clock.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/irq.h>
+#include <calypso/misc.h>
+#include <calypso/uart.h>
+#include <calypso/timer.h>
+
+#include <layer1/sync.h>
+#include <layer1/tpu_window.h>
+
+#include "protocol.h"
+
+struct loader_mem_read {
+ uint8_t cmd;
+ uint8_t nbytes;
+ uint32_t address;
+ uint8_t data[0];
+} __attribute__((__packed__));
+
+uint32_t htonl(uint32_t hostlong) {
+#if BYTE_ORDER==LITTLE_ENDIAN
+ return (hostlong>>24) | ((hostlong&0xff0000)>>8) |
+ ((hostlong&0xff00)<<8) | (hostlong<<24);
+#else
+ return hostlong;
+#endif
+}
+
+uint32_t ntohl(uint32_t hostlong) __attribute__((weak,alias("htonl")));
+
+uint16_t htons(uint16_t hostshort) {
+#if BYTE_ORDER==LITTLE_ENDIAN
+ return ((hostshort>>8)&0xff) | (hostshort<<8);
+#else
+ return hostshort;
+#endif
+}
+
+uint16_t ntohs(uint16_t hostshort) __attribute__((weak,alias("htons")));
+
+#define SCAN
+
+#ifdef SCAN
+/* if scanning is enabled, scan from 0 ... 124 */
+#define BASE_ARFCN 0
+#else
+/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */
+#define BASE_ARFCN 871
+#endif
+
+/* FIXME: We need proper calibrated delay loops at some point! */
+void delay_us(unsigned int us)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < us*4; i++) { i; }
+}
+
+void delay_ms(unsigned int ms)
+{
+ volatile unsigned int i;
+
+ for (i= 0; i < ms*1300; i++) { i; }
+}
+
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+static void key_handler(enum key_codes code, enum key_states state);
+static void cmd_handler(uint8_t dlci, struct msgb *msg);
+
+int flag = 0;
+
+void poweroff(void) {
+ unsigned i;
+ for(i = 0; i < 10; i++) {
+ uart_poll(SERCOMM_UART_NR);
+ delay_ms(10);
+ }
+ twl3025_power_off();
+}
+
+int main(void)
+{
+ /* Always disable wdt (some platforms enable it on boot) */
+ wdog_enable(0);
+
+ /* Initialize TWL3025 for power control */
+ twl3025_init();
+
+ /* Initialize UART without interrupts */
+ uart_init(SERCOMM_UART_NR, 0);
+ uart_baudrate(SERCOMM_UART_NR, UART_115200);
+
+ /* Initialize HDLC subsystem */
+ sercomm_init();
+
+ /* Say hi */
+ puts("\n\nOSMOCOM Calypso loader\n");
+ puts(hr);
+
+ /* Set up a key handler for powering off */
+ keypad_set_handler(&key_handler);
+
+ /* Set up loader communications */
+ sercomm_register_rx_cb(SC_DLCI_LOADER, &cmd_handler);
+
+ /* Wait for events */
+ while (1) {
+ keypad_poll();
+ uart_poll(SERCOMM_UART_NR);
+ }
+
+ /* NOT REACHED */
+
+ twl3025_power_off();
+}
+
+static void cmd_handler(uint8_t dlci, struct msgb *msg) {
+ if(msg->data_len < 1) {
+ return;
+ }
+
+ uint8_t command = 0; //= msgb_get_u8(msg);
+
+ printf("command %u\n", command);
+
+ msgb_free(msg);
+
+ return;
+
+ uint8_t nbytes;
+ uint32_t address;
+
+ struct msgb *reply;
+
+ switch(command) {
+
+ case LOADER_PING:
+
+ printf("ping\n");
+
+ //sercomm_sendmsg(dlci, msg);
+ //msg = NULL;
+
+ break;
+
+ case LOADER_MEM_READ:
+
+ nbytes = msgb_get_u8(msg);
+ address = msgb_get_u32(msg);
+
+ printf("mem read %u @ %p\n", nbytes, (void*)address);
+
+ reply = sercomm_alloc_msgb(6 + nbytes);
+
+ msgb_put_u8(reply, LOADER_MEM_READ);
+ msgb_put_u8(reply, nbytes);
+ msgb_put_u32(reply, address);
+
+ memcpy(msgb_put(reply, nbytes), (void*)address, nbytes);
+
+ sercomm_sendmsg(dlci, reply);
+
+ break;
+
+ }
+
+ if(msg) {
+ msgb_free(msg);
+ }
+}
+
+static void key_handler(enum key_codes code, enum key_states state)
+{
+ if (state != PRESSED)
+ return;
+
+ switch (code) {
+ case KEY_POWER:
+ poweroff();
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/target/firmware/apps/loader/protocol.h b/src/target/firmware/apps/loader/protocol.h
new file mode 100644
index 00000000..afbd2390
--- /dev/null
+++ b/src/target/firmware/apps/loader/protocol.h
@@ -0,0 +1,6 @@
+
+enum loader_command {
+ LOADER_PING,
+ LOADER_MEM_READ,
+ LOADER_MEM_WRITE,
+};
diff --git a/src/target/firmware/board/common/calypso_pwl.S b/src/target/firmware/board/common/calypso_pwl.S
new file mode 100644
index 00000000..90e29bff
--- /dev/null
+++ b/src/target/firmware/board/common/calypso_pwl.S
@@ -0,0 +1,21 @@
+
+/* Calypso PWL driver */
+
+#define ASIC_CONF_REG 0xfffef008
+#define BA_PWL 0xfffe8000
+
+.globl pwl_init
+pwl_init: ldr r1, =ASIC_CONF_REG
+ ldr r2, [r1]
+ orr r2, r2, #0x10 @ set light output to PWL
+ str r2, [r1]
+ ldr r1, =BA_PWL
+ mov r0, #1
+ strb r0, [r1, #1] @ enable clock of PWL unut
+ mov pc, lr
+
+.globl pwl_set_level
+pwl_set_level: ldr r1, =BA_PWL
+ strb r0, [r1]
+ mov pc, lr
+
diff --git a/src/target/firmware/board/common/calypso_uart.S b/src/target/firmware/board/common/calypso_uart.S
new file mode 100644
index 00000000..808cb051
--- /dev/null
+++ b/src/target/firmware/board/common/calypso_uart.S
@@ -0,0 +1,92 @@
+/* uart routines for early assembly code */
+
+#define BA_UART_MODEM 0xFFFF5800
+
+.macro senduart, rd, rx
+ strb \rd, [\rx, #0]
+.endm
+
+.macro busyuart, rd, rx
+1001:
+ @busy waiting until THR is empty
+ ldrb \rd, [\rx, #5] @ read LSR register
+ mov \rd, \rd, lsr #6
+ tst \rd, #1
+ beq 1001b
+.endm
+
+.macro loadsp, rd
+ ldr \rd, =BA_UART_MODEM
+.endm
+
+.section .text
+
+ .align 2
+ .type phexbuf, #object
+phexbuf: .space 12
+ .size phexubf, . - phexbuf
+
+.globl phex
+phex: adr r3, phexbuf
+ mov r2, #0
+ strb r2, [r3, r1]
+1: subs r1, r1, #1
+ movmi r0, r3
+ bmi puts_asm
+ and r2, r0, #15
+ mov r0, r0, lsr #4
+ cmp r2, #10
+ addge r2, r2, #7
+ add r2, r2, #'0'
+ strb r2, [r3, r1]
+ b 1b
+
+.globl puts_asm
+puts_asm: loadsp r3
+1: ldrb r2, [r0], #1
+ teq r2, #0
+ moveq pc, lr
+2: senduart r2, r3
+ busyuart r1, r3
+ teq r2, #'\n'
+ moveq r2, #'\r'
+ beq 2b
+ teq r0, #0
+ bne 1b
+ mov pc, lr
+
+.globl putchar_asm
+putchar_asm:
+ mov r2, r0
+ mov r0, #0
+ loadsp r3
+ b 2b
+
+.globl memdump_asm
+memdump_asm: mov r12, r0
+ mov r10, lr
+ mov r11, #0
+2: mov r0, r11, lsl #2
+ add r0, r0, r12
+ mov r1, #8
+ bl phex
+ mov r0, #':'
+ bl putchar_asm
+1: mov r0, #' '
+ bl putchar_asm
+ ldr r0, [r12, r11, lsl #2]
+ mov r1, #8
+ bl phex
+ and r0, r11, #7
+ teq r0, #3
+ moveq r0, #' '
+ bleq putchar_asm
+ and r0, r11, #7
+ add r11, r11, #1
+ teq r0, #7
+ bne 1b
+ mov r0, #'\n'
+ bl putchar_asm
+ cmp r11, #64
+ blt 2b
+ mov pc, r10
diff --git a/src/target/firmware/board/common/compal_osmoload.lds b/src/target/firmware/board/common/compal_osmoload.lds
new file mode 100644
index 00000000..2765ec22
--- /dev/null
+++ b/src/target/firmware/board/common/compal_osmoload.lds
@@ -0,0 +1,98 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+MEMORY
+{
+ /* compal-loaded binary: text, initialized data */
+ LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000
+ /* compal-loaded binary: unitialized data, stacks, heap */
+ DRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00010000
+ /* remainder of internal ram: this is where we are linking to */
+ IRAM (rw) : ORIGIN = 0x00820000, LENGTH = 0x00020000
+}
+SECTIONS
+{
+ . = 0x820000;
+
+ /* initialization code */
+ . = ALIGN(4);
+ .text.start : {
+ PROVIDE(_start = .);
+ KEEP(*(.text.start))
+ *(.text.start)
+ } > IRAM
+
+ /* exception vectors from 0x80001c to 0x800034 */
+ .text.exceptions 0x80001c : AT (LOADADDR(.text.start) + SIZEOF(.text.start)) {
+ KEEP(*(.text.exceptions))
+ * (.text.exceptions)
+ . = ALIGN(4);
+
+ /* constructors and destructors */
+ . = ALIGN(4);
+ __CTOR_LIST__ = .;
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
+ KEEP(*(SORT(.ctors)))
+ LONG(0) /* end of list */
+ __CTOR_END__ = .;
+ __DTOR_LIST__ = .;
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
+ KEEP(*(SORT(.dtors)))
+ LONG(0) /* end of list */
+ __DTOR_END__ = .;
+ } > LRAM
+ PROVIDE(_exceptions = LOADADDR(.text.exceptions));
+
+ /* code */
+ . = ALIGN(4);
+ .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) :
+ AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) {
+ /* regular code */
+ *(.text*)
+ /* gcc voodoo */
+ *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
+ } > IRAM
+ PROVIDE(_text_start = LOADADDR(.text));
+ PROVIDE(_text_end = LOADADDR(.text) + SIZEOF(.text));
+
+ /* read-only data */
+ . = ALIGN(4);
+ .rodata : {
+ *(.rodata*)
+ } > IRAM
+ PROVIDE(_rodata_start = LOADADDR(.rodata));
+ PROVIDE(_rodata_end = LOADADDR(.rodata) + SIZEOF(.rodata));
+
+ /* initialized data */
+ . = ALIGN(4);
+ .data : {
+ *(.data)
+ } > IRAM
+ PROVIDE(_data_start = LOADADDR(.data));
+ PROVIDE(_data_end = LOADADDR(.data) + SIZEOF(.data));
+
+ /* pic offset tables */
+ . = ALIGN(4);
+ .got : {
+ *(.got)
+ *(.got.plt) *(.igot.plt) *(.got) *(.igot)
+ } > IRAM
+ PROVIDE(_got_start = LOADADDR(.got));
+ PROVIDE(_got_end = LOADADDR(.got) + SIZEOF(.got));
+
+ /* uninitialized data */
+ .bss (NOLOAD) : {
+ . = ALIGN(4);
+ __bss_start = .;
+ *(.bss)
+ } > IRAM
+ . = ALIGN(4);
+ __bss_end = .;
+ PROVIDE(_bss_start = __bss_start);
+ PROVIDE(_bss_end = __bss_end);
+
+ /* end of image */
+ . = ALIGN(4);
+ _end = .;
+ PROVIDE(end = .);
+}
diff --git a/src/target/firmware/board/common/compal_ramload.lds b/src/target/firmware/board/common/compal_ramload.lds
new file mode 100644
index 00000000..00f0d001
--- /dev/null
+++ b/src/target/firmware/board/common/compal_ramload.lds
@@ -0,0 +1,103 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+MEMORY
+{
+ /* compal-loaded binary: our text, initialized data */
+ LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000
+ /* compal-loaded binary: our unitialized data, stacks, heap */
+ IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00010000
+}
+SECTIONS
+{
+ . = 0x800000;
+
+ /* romloader data section, contains passthru interrupt vectors */
+ .compal.loader (NOLOAD) : { . = 0x100; } > LRAM
+
+ /* image signature (prepended by osmocon according to phone type) */
+ .compal.header (NOLOAD) : { . = 4; } > LRAM
+
+ /* initialization code */
+ . = ALIGN(4);
+ .text.start : {
+ PROVIDE(_start = .);
+ KEEP(*(.text.start))
+ *(.text.start)
+ } > LRAM
+
+ /* exception vectors from 0x80001c to 0x800034 */
+ .text.exceptions 0x80001c : AT (LOADADDR(.text.start) + SIZEOF(.text.start)) {
+ KEEP(*(.text.exceptions))
+ * (.text.exceptions)
+ . = ALIGN(4);
+ } > LRAM
+ PROVIDE(_exceptions = LOADADDR(.text.exceptions));
+
+ /* code */
+ . = ALIGN(4);
+ .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) :
+ AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) {
+ /* regular code */
+ *(.text*)
+ /* gcc voodoo */
+ *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
+ . = ALIGN(4);
+
+ /* constructors and destructors */
+ . = ALIGN(4);
+ __CTOR_LIST__ = .;
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
+ KEEP(*(SORT(.ctors)))
+ LONG(0) /* end of list */
+ __CTOR_END__ = .;
+ __DTOR_LIST__ = .;
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
+ KEEP(*(SORT(.dtors)))
+ LONG(0) /* end of list */
+ __DTOR_END__ = .;
+ } > LRAM
+ PROVIDE(_text_start = LOADADDR(.text));
+ PROVIDE(_text_end = LOADADDR(.text) + SIZEOF(.text));
+
+ /* read-only data */
+ . = ALIGN(4);
+ .rodata : {
+ *(.rodata*)
+ } > LRAM
+ PROVIDE(_rodata_start = LOADADDR(.rodata));
+ PROVIDE(_rodata_end = LOADADDR(.rodata) + SIZEOF(.rodata));
+
+ /* initialized data */
+ . = ALIGN(4);
+ .data : {
+ *(.data)
+ } > LRAM
+ PROVIDE(_data_start = LOADADDR(.data));
+ PROVIDE(_data_end = LOADADDR(.data) + SIZEOF(.data));
+
+ /* pic offset tables */
+ . = ALIGN(4);
+ .got : {
+ *(.got)
+ *(.got.plt) *(.igot.plt) *(.got) *(.igot)
+ } > LRAM
+ PROVIDE(_got_start = LOADADDR(.got));
+ PROVIDE(_got_end = LOADADDR(.got) + SIZEOF(.got));
+
+ /* uninitialized data */
+ .bss (NOLOAD) : {
+ . = ALIGN(4);
+ __bss_start = .;
+ *(.bss)
+ } > IRAM
+ . = ALIGN(4);
+ __bss_end = .;
+ PROVIDE(_bss_start = __bss_start);
+ PROVIDE(_bss_end = __bss_end);
+
+ /* end of image */
+ . = ALIGN(4);
+ _end = .;
+ PROVIDE(end = .);
+}
diff --git a/src/target/firmware/board/common/compal_ramload_start.S b/src/target/firmware/board/common/compal_ramload_start.S
new file mode 100644
index 00000000..c89c881c
--- /dev/null
+++ b/src/target/firmware/board/common/compal_ramload_start.S
@@ -0,0 +1,163 @@
+
+ .EQU ARM_MODE_FIQ, 0x11
+ .EQU ARM_MODE_IRQ, 0x12
+ .EQU ARM_MODE_SVC, 0x13
+
+ .EQU I_BIT, 0x80
+ .EQU F_BIT, 0x40
+
+#define TOP_OF_RAM 0x083fff0
+#define FIQ_STACK_SIZE 1024
+#define IRQ_STACK_SIZE 1024
+
+.section .text.start
+
+.globl _start
+_start:
+ /* clear bss section */
+ .global __bss_start
+ .global __bss_end
+ mov r0, #0
+ ldr r1, =__bss_start
+ ldr r2, =__bss_end
+2: cmp r1, r2
+ strlo r0, [r1], #4
+ blo 2b
+
+ /* initialize stacks, starting at TOP_OF_RAM */
+ ldr r0, =TOP_OF_RAM
+
+ /* initialize FIQ stack */
+ msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT
+ mov r13, r0
+ sub r0, r0, #FIQ_STACK_SIZE
+
+ /* initialize IRQ stack */
+ msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT
+ mov r13, r0
+ sub r0, r0, #IRQ_STACK_SIZE
+
+ /* initialize supervisor stack */
+ msr CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT
+ mov r13, r0
+
+ /* set backlight to moderate level */
+ bl pwl_init
+ mov r0, #50
+ bl pwl_set_level
+
+ /* test uart output */
+ @ldr r0, =string
+ @bl puts_asm
+
+ /* dump some memory */
+ @ldr r0, =0xfffef000
+ @bl memdump
+ @ldr r0, =0xfffffe00
+ @bl memdump
+
+ /* call constructor functions */
+ ldr r0, _ctor_list
+ ldr r1, _ctor_end
+ bl do_global_ctors
+
+ /* jump to main */
+ ldr pc, _jump_main
+
+ /* endless loop at end of program */
+_end: b _end
+ b _start
+
+_jump_main: .word main
+_ctor_list: .word __CTOR_LIST__
+_ctor_end: .word __CTOR_END__
+
+/* handler for all kinds of aborts */
+handle_abort:
+ @ print the PC we would jump back to...
+ sub lr, lr, #4 @ we assume to be ARM32
+
+ mov r0, lr
+ mov r1, #8
+ bl phex
+
+ @ print abort message
+ mov r0, #'A'
+ bl putchar_asm
+ mov r0, #'B'
+ bl putchar_asm
+ mov r0, #'O'
+ bl putchar_asm
+ mov r0, #'R'
+ bl putchar_asm
+ mov r0, #'T'
+ bl putchar_asm
+
+ @ disable IRQ and FIQ
+ msr CPSR_c, #I_BIT | F_BIT
+
+0: @ dead
+ b 0b
+
+/* entry point for IRQs */
+irq_entry:
+ /* Adjust and save LR_irq in IRQ stack */
+ sub lr, lr, #4
+ stmfd sp!, {lr}
+
+ /* Save SPSR for nested interrupt */
+ mrs r14, SPSR
+ stmfd sp!, {r14}
+
+ /* Call the interrupt handler C function */
+ stmfd sp!, {r0-r4, r12}
+ bl irq
+ ldmfd sp!, {r0-r4, r12}
+
+ /* Restore SPSR_irq from IRQ stack */
+ ldmia sp!, {r14}
+ msr SPSR_cxsf, r14
+
+ /* Restore adjusted LR_irq from IRQ stack directly in the PC */
+ ldmia sp!, {pc}^
+
+/* entry point for FIQs */
+fiq_entry:
+ /* Adjust and save LR_irq in IRQ stack */
+ sub lr, lr, #4
+ stmfd sp!, {lr}
+
+ /* Save SPSR for nested interrupt */
+ mrs r14, SPSR
+ stmfd sp!, {r14}
+
+ /* Call the interrupt handler C function */
+ stmfd sp!, {r0-r4, r12}
+ bl fiq
+ ldmfd sp!, {r0-r4, r12}
+
+ /* Restore SPSR_irq from IRQ stack */
+ ldmia sp!, {r14}
+ msr SPSR_cxsf, r14
+
+ /* Restore adjusted LR_irq from IRQ stack directly in the PC */
+ ldmia sp!, {pc}^
+
+/* Exception Vectors like they are needed for the exception vector
+ indirection of the internal boot ROM. The following section must be liked
+ to appear at 0x80'001c */
+.section .text.exceptions
+_undef_instr:
+ b handle_abort
+_sw_interr:
+ b _sw_interr
+_prefetch_abort:
+ b handle_abort
+_data_abort:
+ b handle_abort
+_reserved:
+ b _reserved
+_irq:
+ b irq_entry
+_fiq:
+ b fiq_entry
diff --git a/src/target/firmware/board/common/rffe_compal_dualband.c b/src/target/firmware/board/common/rffe_compal_dualband.c
new file mode 100644
index 00000000..bfd3d98e
--- /dev/null
+++ b/src/target/firmware/board/common/rffe_compal_dualband.c
@@ -0,0 +1,80 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rffe.h>
+#include <calypso/tsp.h>
+#include <rf/trf6151.h>
+
+/* This is a value that has been measured on the C123 by Harald: 71dBm,
+ it is the difference between the input level at the antenna and what
+ the DSP reports, subtracted by the total gain of the TRF6151 */
+#define SYSTEM_INHERENT_GAIN 71
+
+/* describe how the RF frontend is wired on the Motorola E88 board (C117/C118/C121/C123) */
+
+#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */
+#define PA_ENABLE TSPACT(1) /* Enable the Power Amplifier */
+#define TRENA TSPACT(6) /* Transmit Enable (Antenna Switch) */
+#define GSM_TXEN TSPACT(8) /* GSM (as opposed to DCS) Transmit */
+
+#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */
+#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */
+
+/* switch RF Frontend Mode */
+void rffe_mode(enum gsm_band band, int tx)
+{
+ uint16_t tspact = tsp_act_state();
+
+ /* First we mask off all bits from the state cache */
+ tspact &= ~PA_ENABLE;
+ tspact |= TRENA | GSM_TXEN; /* low-active */
+
+#ifdef CONFIG_TX_ENABLE
+ /* Then we selectively set the bits on, if required */
+ if (tx) {
+ tspact &= ~TRENA;
+ if (band == GSM_BAND_900)
+ tspact &= ~GSM_TXEN;
+ tspact |= PA_ENABLE; /* Dieter: TODO */
+ }
+#endif /* TRANSMIT_SUPPORT */
+
+ tsp_act_update(tspact);
+}
+
+#define MCU_SW_TRACE 0xfffef00e
+#define ARM_CONF_REG 0xfffef006
+
+void rffe_init(void)
+{
+ uint16_t reg;
+
+ reg = readw(ARM_CONF_REG);
+ reg &= ~ (1 << 5); /* TSPACT6 I/O function, not nCS6 */
+ writew(reg, ARM_CONF_REG);
+
+ reg = readw(MCU_SW_TRACE);
+ reg &= ~(1 << 5); /* TSPACT8 I/O function, not nMREQ */
+ writew(reg, MCU_SW_TRACE);
+}
+
+uint8_t rffe_get_gain(void)
+{
+ return trf6151_get_gain();
+}
+
+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_set_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/common/rffe_gta0x_triband.c b/src/target/firmware/board/common/rffe_gta0x_triband.c
new file mode 100644
index 00000000..a21cc612
--- /dev/null
+++ b/src/target/firmware/board/common/rffe_gta0x_triband.c
@@ -0,0 +1,96 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rffe.h>
+#include <calypso/tsp.h>
+#include <rf/trf6151.h>
+
+/* This is a value that has been measured on the C123 by Harald: 71dBm,
+ it is the difference between the input level at the antenna and what
+ the DSP reports, subtracted by the total gain of the TRF6151 */
+#define SYSTEM_INHERENT_GAIN 71
+
+/* describe how the RF frontend is wired on the Openmoko GTA0x boards */
+
+#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */
+#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */
+#define GSM_TXEN TSPACT(3) /* PA GSM switch, low-active */
+
+/* All VCn controls are low-active */
+#define ASM_VC1 TSPACT(2) /* Antenna switch VC1 */
+#define ASM_VC2 TSPACT(1) /* Antenna switch VC2 */
+#define ASM_VC3 TSPACT(4) /* Antenna switch VC3 */
+
+#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */
+#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */
+
+/* switch RF Frontend Mode */
+void rffe_mode(enum gsm_band band, int tx)
+{
+ uint16_t tspact = tsp_act_state();
+
+ /* First we mask off all bits from the state cache */
+ tspact &= ~PA_ENABLE;
+ tspact |= GSM_TXEN; /* low-active */
+ tspact |= ASM_VC1 | ASM_VC2 | ASM_VC3; /* low-active */
+
+ switch (band) {
+ case GSM_BAND_850:
+ case GSM_BAND_900:
+ case GSM_BAND_1800:
+ break;
+ case GSM_BAND_1900:
+ tspact &= ~ASM_VC2;
+ break;
+ default:
+ /* TODO return/signal error here */
+ break;
+ }
+
+#ifdef CONFIG_TX_ENABLE
+ /* Then we selectively set the bits on, if required */
+ if (tx) {
+ // TODO: Implement tx
+ }
+#endif /* TRANSMIT_SUPPORT */
+
+ tsp_act_update(tspact);
+}
+
+#define MCU_SW_TRACE 0xfffef00e
+#define ARM_CONF_REG 0xfffef006
+
+void rffe_init(void)
+{
+ uint16_t reg;
+
+ reg = readw(ARM_CONF_REG);
+ reg &= ~ (1 << 7); /* TSPACT4 I/O function, not nRDYMEM */
+ writew(reg, ARM_CONF_REG);
+
+ reg = readw(MCU_SW_TRACE);
+ reg &= ~(1 << 1); /* TSPACT9 I/O function, not MAS(1) */
+ writew(reg, MCU_SW_TRACE);
+}
+
+uint8_t rffe_get_gain(void)
+{
+ return trf6151_get_gain();
+}
+
+const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN;
+
+#define to_dbm8(x) ((x)*8)
+/* 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_set_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/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c
new file mode 100644
index 00000000..94586754
--- /dev/null
+++ b/src/target/firmware/board/compal_e88/init.c
@@ -0,0 +1,132 @@
+/* Initialization for the Compal E88 (Motorola C115...C123) */
+
+/* (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 <cfi_flash.h>
+
+#include <calypso/irq.h>
+#include <calypso/clock.h>
+#include <calypso/dma.h>
+#include <calypso/rtc.h>
+#include <calypso/timer.h>
+#include <calypso/uart.h>
+
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include <display.h>
+
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define ARMIO_CNTL_REG 0xfffe4804
+#define ASIC_CONF_REG 0xfffef008
+
+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 |= 0x1080; /* 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(ARMIO_CNTL_REG);
+ reg &= ~(1 << 3);
+ writew(reg, ARMIO_CNTL_REG);
+
+ /* LCD Set I/O(3) output low */
+ reg = readw(ARMIO_LATCH_OUT);
+ reg &= ~(1 << 3);
+ writew(reg, ARMIO_LATCH_OUT);
+}
+
+static void __ctor_board board_init(void)
+{
+ /* FIXME: this needs to go to board_e99/init.c once we have it */
+ wdog_enable(0);
+
+ static cfi_flash_t flash;
+ // XXX: move after mapping initialization and use final address
+ flash_init(&flash, 0x00000000);
+
+ calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1);
+ 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);
+
+ board_io_init();
+
+ /* Enable bootrom mapping to route exception vectors to RAM */
+ calypso_bootrom(1);
+ calypso_exceptions_install();
+
+ irq_init();
+
+ /* initialize MODEM UART to be used for sercomm*/
+ uart_init(SERCOMM_UART_NR, 1);
+ uart_baudrate(SERCOMM_UART_NR, 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(CONS_UART_NR, 1);
+ uart_baudrate(CONS_UART_NR, UART_115200);
+
+ hwtimer_init();
+
+ dma_init();
+ rtc_init();
+
+ timer_init();
+
+ /* Initialize LCD driver (uses I2C) */
+ display = &st7558_display;
+ display_init();
+
+ /* Initialize keypad driver */
+ keypad_init(1);
+
+ /* Initialize ABB driver (uses SPI) */
+ twl3025_init();
+}
diff --git a/src/target/firmware/board/compal_e99/init.c b/src/target/firmware/board/compal_e99/init.c
new file mode 100644
index 00000000..b2cdd7b1
--- /dev/null
+++ b/src/target/firmware/board/compal_e99/init.c
@@ -0,0 +1,130 @@
+/* Initialization for the Compal E99 (Motorola C155) */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 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 <cfi_flash.h>
+
+#include <calypso/irq.h>
+#include <calypso/clock.h>
+#include <calypso/dma.h>
+#include <calypso/rtc.h>
+#include <calypso/timer.h>
+#include <calypso/uart.h>
+
+#include <comm/sercomm.h>
+
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include <display.h>
+
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define ARMIO_CNTL_REG 0xfffe4804
+#define ASIC_CONF_REG 0xfffef008
+
+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 << 12) | (1 << 10) | (1 << 7) | (1 << 1)) ;
+ /* don't set function pins to I2C Mode, C155 uses UWire */
+ /* 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 and enable C155 backlight (IO1) */
+ /* FIXME: Put the display backlight control to backlight.c */
+ reg = readw(ARMIO_CNTL_REG);
+ reg &= ~( (1 << 3) | (1 << 1));
+ writew(reg, ARMIO_CNTL_REG);
+
+ /* LCD Set I/O(3) output low */
+ reg = readw(ARMIO_LATCH_OUT);
+ reg &= ~(1 << 3);
+ reg |= (1 << 1);
+ writew(reg, ARMIO_LATCH_OUT);
+}
+
+static void __ctor_board board_init(void)
+{
+ /* FIXME: this needs to go to board_e99/init.c once we have it */
+ wdog_enable(0);
+
+ static cfi_flash_t flash;
+ // XXX: move after mapping initialization and use final address
+ flash_init(&flash, 0x00000000);
+
+ calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1);
+ 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);
+
+ board_io_init();
+
+ /* Enable bootrom mapping to route exception vectors to RAM */
+ calypso_bootrom(1);
+ calypso_exceptions_install();
+
+ irq_init();
+
+ /* initialize MODEM UART to be used for sercomm*/
+ uart_init(SERCOMM_UART_NR, 1);
+ uart_baudrate(SERCOMM_UART_NR, 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(CONS_UART_NR, 1);
+ uart_baudrate(CONS_UART_NR, UART_115200);
+
+ hwtimer_init();
+
+ dma_init();
+ rtc_init();
+
+ /* Initialize LCD driver (uses UWire) */
+ display = &ssd1783_display;
+ display_init();
+
+ keypad_init(1);
+
+ /* Initialize ABB driver (uses SPI) */
+ twl3025_init();
+}
diff --git a/src/target/firmware/board/gta0x/init.c b/src/target/firmware/board/gta0x/init.c
new file mode 100644
index 00000000..4dca2e1f
--- /dev/null
+++ b/src/target/firmware/board/gta0x/init.c
@@ -0,0 +1,132 @@
+/* Initialization for the Openmoko Freerunner modem */
+
+/* (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 <cfi_flash.h>
+
+#include <calypso/irq.h>
+#include <calypso/clock.h>
+#include <calypso/dma.h>
+#include <calypso/rtc.h>
+#include <calypso/timer.h>
+#include <calypso/uart.h>
+
+#include <comm/sercomm.h>
+#include <comm/timer.h>
+
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+#include <display.h>
+
+#define ARMIO_LATCH_OUT 0xfffe4802
+#define ARMIO_CNTL_REG 0xfffe4804
+#define ASIC_CONF_REG 0xfffef008
+
+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 |= 0x1080; /* 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(ARMIO_CNTL_REG);
+ reg &= ~(1 << 3);
+ writew(reg, ARMIO_CNTL_REG);
+
+ /* LCD Set I/O(3) output low */
+ reg = readw(ARMIO_LATCH_OUT);
+ reg &= ~(1 << 3);
+ writew(reg, ARMIO_LATCH_OUT);
+}
+
+static void __ctor_board board_init(void)
+{
+ /* FIXME: this needs to go to board_e99/init.c once we have it */
+ wdog_enable(0);
+
+ static cfi_flash_t flash;
+ // XXX: move after mapping initialization and use final address
+ flash_init(&flash, 0x00000000);
+
+ calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1);
+ calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1);
+ 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);
+
+ board_io_init();
+
+ /* Enable bootrom mapping to route exception vectors to RAM */
+ calypso_bootrom(1);
+ calypso_exceptions_install();
+
+ irq_init();
+
+ /* initialize MODEM UART to be used for sercomm*/
+ uart_init(SERCOMM_UART_NR, 1);
+ uart_baudrate(SERCOMM_UART_NR, 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(CONS_UART_NR, 1);
+ uart_baudrate(CONS_UART_NR, UART_115200);
+
+ hwtimer_init();
+
+ dma_init();
+ rtc_init();
+
+ timer_init();
+
+ /* Initialize LCD driver (uses I2C) */
+ display = &st7558_display;
+ display_init();
+
+ /* Initialize keypad driver */
+ keypad_init(1);
+
+ /* Initialize ABB driver (uses SPI) */
+ twl3025_init();
+}
diff --git a/src/target/firmware/board/manifest.c b/src/target/firmware/board/manifest.c
new file mode 100644
index 00000000..025a7224
--- /dev/null
+++ b/src/target/firmware/board/manifest.c
@@ -0,0 +1,7 @@
+
+#include "manifest.h"
+
+const char *manifest_application = APPLICATION;
+const char *manifest_revision = GIT_REVISION;
+const char *manifest_board = BOARD;
+const char *manifest_environment = ENVIRONMENT;
diff --git a/src/target/firmware/calypso/Makefile b/src/target/firmware/calypso/Makefile
new file mode 100644
index 00000000..ef8a6a7a
--- /dev/null
+++ b/src/target/firmware/calypso/Makefile
@@ -0,0 +1,4 @@
+
+LIBRARIES+=calypso
+calypso_DIR=calypso
+calypso_SRCS=arm.c clock.c dma.c dsp.c du.c i2c.c irq.c rtc.c spi.c tpu.c tsp.c keypad.c misc.c timer.c backlight.c uart.c uwire.c
diff --git a/src/target/firmware/calypso/arm.c b/src/target/firmware/calypso/arm.c
new file mode 100644
index 00000000..8794ee35
--- /dev/null
+++ b/src/target/firmware/calypso/arm.c
@@ -0,0 +1,26 @@
+
+/* enable IRQ+FIQ interrupts */
+void arm_enable_interrupts (void)
+{
+ unsigned long temp;
+ __asm__ __volatile__("mrs %0, cpsr\n"
+ "bic %0, %0, #0xc0\n"
+ "msr cpsr_c, %0"
+ : "=r" (temp)
+ :
+ : "memory");
+}
+
+/* disable IRQ/FIQ interrupts
+ * returns true if interrupts had been enabled before we disabled them */
+int arm_disable_interrupts(void)
+{
+ unsigned long old,temp;
+ __asm__ __volatile__("mrs %0, cpsr\n"
+ "orr %1, %0, #0xc0\n"
+ "msr cpsr_c, %1"
+ : "=r" (old), "=r" (temp)
+ :
+ : "memory");
+ return (old & 0x80) == 0;
+}
diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c
new file mode 100644
index 00000000..e2ff29cc
--- /dev/null
+++ b/src/target/firmware/calypso/backlight.c
@@ -0,0 +1,67 @@
+/* Calypso DBB internal PWL (Pulse Width / Light) Driver */
+
+/* (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 <memory.h>
+
+#define BASE_ADDR_PWL 0xfffe8000
+#define PWL_REG(m) (BASE_ADDR_PWL + (m))
+
+#define ASIC_CONF_REG 0xfffef008
+#define LIGHT_LEVEL_REG 0xfffe4810
+
+enum pwl_reg {
+ PWL_LEVEL = 0,
+ PWL_CTRL = 2,
+};
+
+#define ASCONF_PWL_ENA (1 << 4)
+
+void bl_mode_pwl(int on)
+{
+ uint16_t reg;
+
+ reg = readw(ASIC_CONF_REG);
+
+ if (on) {
+ writeb(0x01, PWL_REG(PWL_CTRL));
+ /* Switch pin from LT to PWL */
+ reg |= ASCONF_PWL_ENA;
+ writew(reg, ASIC_CONF_REG);
+ } else {
+ /* Switch pin from PWL to LT */
+ reg |= ~ASCONF_PWL_ENA;
+ writew(reg, ASIC_CONF_REG);
+ writeb(0x00, PWL_REG(PWL_CTRL));
+ }
+}
+
+void bl_level(uint8_t level)
+{
+ if (readw(ASIC_CONF_REG) & ASCONF_PWL_ENA) {
+ writeb(level, PWL_REG(PWL_LEVEL));
+ } else {
+ /* we need to scale the light level, as the
+ * ARMIO light controller only knows 0..63 */
+ writeb(level>>2, LIGHT_LEVEL_REG);
+ }
+}
diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c
new file mode 100644
index 00000000..246b6e00
--- /dev/null
+++ b/src/target/firmware/calypso/clock.c
@@ -0,0 +1,200 @@
+/* Driver for Calypso clock management */
+
+/* (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>
+
+//#define DEBUG
+#include <debug.h>
+
+#include <memory.h>
+#include <calypso/clock.h>
+
+#define REG_DPLL 0xffff9800
+#define DPLL_LOCK (1 << 0)
+#define DPLL_BREAKLN (1 << 1)
+#define DPLL_BYPASS_DIV_SHIFT 2 /* 2 bits */
+#define DPLL_PLL_ENABLE (1 << 4)
+#define DPLL_PLL_DIV_SHIFT 5 /* 2 bits */
+#define DPLL_PLL_MULT_SHIFT 7 /* 5 bits */
+#define DPLL_TEST (1 << 12)
+#define DPLL_IOB (1 << 13) /* Initialize on break */
+#define DPLL_IAI (1 << 14) /* Initialize after Idle */
+
+#define BASE_ADDR_CLKM 0xfffffd00
+#define CLKM_REG(m) (BASE_ADDR_CLKM+(m))
+
+enum clkm_reg {
+ CNTL_ARM_CLK = 0,
+ CNTL_CLK = 2,
+ CNTL_RST = 4,
+ CNTL_ARM_DIV = 8,
+};
+
+/* CNTL_ARM_CLK */
+#define ARM_CLK_BIG_SLEEP (1 << 0) /* MCU Master Clock enabled? */
+#define ARM_CLK_CLKIN_SEL0 (1 << 1) /* MCU source clock (0 = DPLL output, 1 = VTCXO or CLKIN */
+#define ARM_CLK_CLKIN_SEL (1 << 2) /* 0 = VTCXO or 1 = CLKIN */
+#define ARM_CLK_MCLK_DIV5 (1 << 3) /* enable 1.5 or 2.5 division factor */
+#define ARM_CLK_MCLK_DIV_SHIFT 4 /* 3 bits */
+#define ARM_CLK_DEEP_POWER_SHIFT 8
+#define ARM_CLK_DEEP_SLEEP 12
+
+/* CNTL_CLK */
+#define CLK_IRQ_CLK_DIS (1 << 0) /* IRQ clock control (0 always, 1 according ARM_MCLK_EN) */
+#define CLK_BRIDGE_CLK_DIS (1 << 1)
+#define CLK_TIMER_CLK_DIS (1 << 2)
+#define CLK_DPLL_DIS (1 << 3) /* 0: DPLL is not stopped during SLEEP */
+#define CLK_CLKOUT_EN (1 << 4) /* Enable CLKOUT output pins */
+#define CLK_EN_IDLE3_FLG (1 << 5) /* DSP idle flag control (1 =
+ * SAM/HOM register forced to HOM when DSP IDLE3) */
+#define CLK_VCLKOUT_DIV2 (1 << 6) /* 1: VCLKOUT-FR is divided by 2 */
+#define CLK_VTCXO_DIV2 (1 << 7) /* 1: VTCXO is dividied by 2 */
+
+#define BASE_ADDR_MEMIF 0xfffffb00
+#define MEMIF_REG(x) (BASE_ADDR_MEMIF+(x))
+
+enum memif_reg {
+ API_RHEA_CTL = 0x0e,
+ EXTRA_CONF = 0x10,
+};
+
+static void dump_reg16(uint32_t addr, char *name)
+{
+ printf("%s=0x%04x\n", name, readw(addr));
+}
+
+void calypso_clk_dump(void)
+{
+ dump_reg16(REG_DPLL, "REG_DPLL");
+ dump_reg16(CLKM_REG(CNTL_ARM_CLK), "CNTL_ARM_CLK");
+ dump_reg16(CLKM_REG(CNTL_CLK), "CNTL_CLK");
+ dump_reg16(CLKM_REG(CNTL_RST), "CNTL_RST");
+ dump_reg16(CLKM_REG(CNTL_ARM_DIV), "CNTL_ARM_DIV");
+}
+
+void calypso_pll_set(uint16_t inp)
+{
+ uint8_t mult = inp >> 8;
+ uint8_t div = inp & 0xff;
+ uint16_t reg = readw(REG_DPLL);
+
+ reg &= ~0x0fe0;
+ reg |= (div & 0x3) << DPLL_PLL_DIV_SHIFT;
+ reg |= (mult & 0x1f) << DPLL_PLL_MULT_SHIFT;
+ reg |= DPLL_PLL_ENABLE;
+
+ writew(reg, REG_DPLL);
+}
+
+void calypso_reset_set(enum calypso_rst calypso_rst, int active)
+{
+ uint8_t reg = readb(CLKM_REG(CNTL_RST));
+
+ if (active)
+ reg |= calypso_rst;
+ else
+ reg &= ~calypso_rst;
+
+ writeb(reg, CLKM_REG(CNTL_RST));
+}
+
+int calypso_reset_get(enum calypso_rst calypso_rst)
+{
+ uint8_t reg = readb(CLKM_REG(CNTL_RST));
+
+ if (reg & calypso_rst)
+ return 1;
+ else
+ return 0;
+}
+
+void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div)
+{
+ uint16_t cntl_clock = readw(CLKM_REG(CNTL_CLK));
+ uint16_t cntl_arm_clk = readw(CLKM_REG(CNTL_ARM_CLK));
+
+ /* First set the vtcxo_div2 */
+ cntl_clock &= ~CLK_VCLKOUT_DIV2;
+ if (vtcxo_div2)
+ cntl_clock |= CLK_VTCXO_DIV2;
+ else
+ cntl_clock &= ~CLK_VTCXO_DIV2;
+ writew(cntl_clock, CLKM_REG(CNTL_CLK));
+
+ /* Then configure the MCLK divider */
+ cntl_arm_clk &= ~ARM_CLK_CLKIN_SEL0;
+ if (mclk_div & 0x80) {
+ mclk_div &= ~0x80;
+ cntl_arm_clk |= ARM_CLK_MCLK_DIV5;
+ } else
+ cntl_arm_clk &= ~ARM_CLK_MCLK_DIV5;
+ cntl_arm_clk &= ~(0x7 << ARM_CLK_MCLK_DIV_SHIFT);
+ cntl_arm_clk |= (mclk_div << ARM_CLK_MCLK_DIV_SHIFT);
+ writew(cntl_arm_clk, CLKM_REG(CNTL_ARM_CLK));
+
+ /* Then finally set the PLL */
+ calypso_pll_set(inp);
+}
+
+void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws,
+ enum calypso_mem_width width, int we)
+{
+ writew((ws & 0x1f) | ((width & 3) << 5) | ((we & 1) << 7),
+ BASE_ADDR_MEMIF + bank);
+}
+
+void calypso_bootrom(int enable)
+{
+ uint16_t conf = readw(MEMIF_REG(EXTRA_CONF));
+
+ conf |= (3 << 8);
+
+ if (enable)
+ conf &= ~(1 << 9);
+
+ writew(conf, MEMIF_REG(EXTRA_CONF));
+}
+
+void calypso_debugunit(int enable)
+{
+ uint16_t conf = readw(MEMIF_REG(EXTRA_CONF));
+
+ if (enable)
+ conf &= ~(1 << 11);
+ else
+ conf |= (1 << 11);
+
+ writew(conf, MEMIF_REG(EXTRA_CONF));
+}
+
+#define REG_RHEA_CNTL 0xfffff900
+#define REG_API_CNTL 0xfffff902
+#define REG_ARM_RHEA 0xfffff904
+
+void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout,
+ uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1)
+{
+ writew(fac0 | (fac1 << 4) | (timeout << 8), REG_RHEA_CNTL);
+ writew(ws_h | (ws_l << 5), REG_API_CNTL);
+ writew(w_en0 | (w_en1 << 1), REG_ARM_RHEA);
+}
diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c
new file mode 100644
index 00000000..35c5be82
--- /dev/null
+++ b/src/target/firmware/calypso/dma.c
@@ -0,0 +1,44 @@
+/* Driver for Calypso DMA controller */
+
+/* (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 <memory.h>
+
+#define BASE_ADDR_DMA 0xfffffc00
+
+enum dma_reg {
+ CONTROLLER_CONF = 0x00,
+ ALLOC_CONFIG = 0x02,
+};
+#define DMA_REG(m) (BASE_ADDR_DMA + (m))
+
+#define DMA_RAD(x) DMA_REG((x)*0x10 + 0x0)
+#define DMA_RDPATH(x) DMA_REG((x)*0x10 + 0x2)
+#define DMA_AAD(x) DMA_REG((x)*0x10 + 0x4)
+#define DMA_ALGTH(x) DMA_REG((x)*0x10 + 0x6)
+#define DMA_CTRL(x) DMA_REG((x)*0x10 + 0x8)
+#define DMA_CUR_OFF_API(x) DMA_REG((x)*0x10 + 0xa)
+
+void dma_init(void)
+{
+ /* DMA 1 (RIF Tx), 2 (RIF Rx) allocated to DSP, all others to ARM */
+ writew(0x000c, DMA_REG(ALLOC_CONFIG));
+}
diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c
new file mode 100644
index 00000000..9cae1f6d
--- /dev/null
+++ b/src/target/firmware/calypso/dsp.c
@@ -0,0 +1,465 @@
+#define DEBUG
+/* Driver for the Calypso integrated DSP */
+
+/* (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 <delay.h>
+#include <memory.h>
+#include <calypso/clock.h>
+#include <calypso/dsp.h>
+#include <calypso/dsp_api.h>
+#include <calypso/tpu.h>
+
+#include <abb/twl3025.h>
+
+#define REG_API_CONTROL 0xfffe0000
+#define APIC_R_SMODE_HOM (1 << 1) /* API is configured in HOM mode */
+#define APIC_R_HINT (1 << 3) /* Host processor interrupt (DSP->MCU) */
+#define APIC_W_DSPINT (1 << 2) /* ARM issues interrupt to DSP */
+
+#define REG_API_WS 0xfffff902 /* Number of wait states for ARM access to API memory */
+#define REG_ARM_RHEA_CTL 0xfffff904 /* Write buffer bypassing */
+#define REG_EXT_RHEA_CTL 0xfffff906 /* Some timeout */
+
+#define API_SIZE 0x2000U /* in words */
+
+#define BASE_API_RAM 0xffd00000 /* Base address of API RAM form ARM point of view */
+
+#define DSP_BASE_API 0x0800 /* Base address of API RAM for DSP */
+#define DSP_BASE_API_MIRROR 0xe000 /* Base address of API RAM for DSP (API boot mirrot */
+#define DSP_START 0x7000 /* DSP Start address */
+
+/* Boot loader */
+#define BL_CMD_STATUS (BASE_API_RAM + 0x0ffe) /* Status / Command var */
+#define BL_ADDR_LO (BASE_API_RAM + 0x0ffc) /* Address (16 lsbs) */
+#define BL_ADDR_HI (BASE_API_RAM + 0x0ff8) /* Address (ext page bits) */
+#define BL_SIZE (BASE_API_RAM + 0x0ffa) /* Size */
+
+#define BL_MAX_BLOCK_SIZE 0x7F0 /* Maximum size of copied block */
+
+ /* Possible values for the download status */
+#define BL_STATUS_NA 0
+#define BL_STATUS_IDLE 1
+#define BL_CMD_COPY_BLOCK 2
+#define BL_CMD_COPY_MODE 4
+
+#define BL_MODE_PROG_WRITE 0
+#define BL_MODE_DATA_WRITE 1
+#define BL_MODE_PROG_READ 2
+#define BL_MODE_DATA_READ 3
+#define BL_MODE_PROM_READ 4
+#define BL_MODE_DROM_READ 5
+
+
+struct dsp_section {
+ uint32_t addr; /* addr for DSP */
+ uint32_t size; /* size in words */
+ const uint16_t *data;
+};
+
+#include "dsp_params.c"
+#include "dsp_bootcode.c"
+#include "dsp_dumpcode.c"
+
+struct dsp_api dsp_api = {
+ .ndb = (T_NDB_MCU_DSP *) BASE_API_NDB,
+ .db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0,
+ .db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0,
+ .param = (T_PARAM_MCU_DSP *) BASE_API_PARAM,
+ .r_page = 0,
+ .w_page = 0,
+};
+
+
+void dsp_dump_version(void)
+{
+ printf("DSP Download Status: 0x%04x\n", readw(BL_CMD_STATUS));
+ printf("DSP API Version: 0x%04x 0x%04x\n",
+ dsp_api.ndb->d_version_number1, dsp_api.ndb->d_version_number2);
+}
+
+static void dsp_bl_wait_ready(void)
+{
+ while (readw(BL_CMD_STATUS) != BL_STATUS_IDLE);
+}
+
+static int dsp_upload_sections_api(const struct dsp_section *sec, uint16_t dsp_base_api)
+{
+ for (; sec->data; sec++) {
+ unsigned int i;
+ volatile uint16_t *dptr;
+
+ if (sec->addr & ~((1<<16)-1)) /* 64k max addr */
+ return -1;
+ if (sec->addr < dsp_base_api)
+ return -1;
+ if ((sec->addr + sec->size) > (dsp_base_api + API_SIZE))
+ return -1;
+
+ dptr = (volatile uint16_t *)(BASE_API_RAM + ((sec->addr - dsp_base_api) * sizeof(uint16_t)));
+ for (i=0; i<sec->size; i++)
+ *dptr++ = sec->data[i];
+ }
+
+ /* FIXME need eioio or wb ? */
+
+ return 0;
+}
+
+static void dsp_pre_boot(const struct dsp_section *bootcode)
+{
+ dputs("Assert DSP into Reset\n");
+ calypso_reset_set(RESET_DSP, 1);
+
+ if (bootcode) {
+ dputs("Loading initial DSP bootcode (API boot mode)\n");
+ dsp_upload_sections_api(dsp_bootcode, DSP_BASE_API_MIRROR);
+
+ writew(BL_STATUS_NA, BL_CMD_STATUS);
+ } else
+ delay_ms(10);
+
+ dputs("Releasing DSP from Reset\n");
+ calypso_reset_set(RESET_DSP, 0);
+
+ /* Wait 10 us */
+ delay_ms(100);
+
+ dsp_bl_wait_ready();
+}
+
+static void dsp_set_params(int16_t *param_tab, int param_size)
+{
+ int i;
+ int16_t *param_ptr = (int16_t *) BASE_API_PARAM;
+
+ /* Start DSP up to bootloader */
+ dsp_pre_boot(dsp_bootcode);
+
+ /* FIXME: Implement Patch download, if any */
+
+ dputs("Setting some dsp_api.ndb values\n");
+ dsp_api.ndb->d_background_enable = 0;
+ dsp_api.ndb->d_background_abort = 0;
+ dsp_api.ndb->d_background_state = 0;
+ dsp_api.ndb->d_debug_ptr = 0x0074;
+ dsp_api.ndb->d_debug_bk = 0x0001;
+ dsp_api.ndb->d_pll_config = 0x154; //C_PLL_CONFIG;
+ dsp_api.ndb->p_debug_buffer = 0x17ff; //C_DEBUG_BUFFER_ADD;
+ dsp_api.ndb->d_debug_buffer_size = 7; //C_DEBUG_BUFFER_SIZE;
+ dsp_api.ndb->d_debug_trace_type = 0; //C_DEBUG_TRACE_TYPE;
+ dsp_api.ndb->d_dsp_state = 3; //C_DSP_IDLE3;
+ dsp_api.ndb->d_audio_gain_ul = 0;
+ dsp_api.ndb->d_audio_gain_dl = 0;
+ dsp_api.ndb->d_es_level_api = 0x5213;
+ dsp_api.ndb->d_mu_api = 0x5000;
+
+ dputs("Setting API NDB parameters\n");
+ for (i = 0; i < param_size; i ++)
+ *param_ptr++ = param_tab[i];
+
+ dsp_dump_version();
+
+ dputs("Finishing download phase\n");
+ writew(0, BL_SIZE);
+ writew(DSP_START, BL_ADDR_LO);
+ writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS);
+
+ dsp_dump_version();
+}
+
+void dsp_api_memset(uint16_t *ptr, int octets)
+{
+ uint16_t i;
+ for (i = 0; i < octets / sizeof(uint16_t); i++)
+ *ptr++ = 0;
+}
+
+static void dsp_ndb_init(void)
+{
+ T_NDB_MCU_DSP *ndb = dsp_api.ndb;
+ uint8_t i;
+
+ #define APCDEL_DOWN (2+0) // minimum value: 2
+ #define APCDEL_UP (6+3+1) // minimum value: 6
+
+ /* load APC ramp: set to "no ramp" so that there will be no output if
+ * not properly initialised at some other place. */
+ for (i = 0; i < 16; i++)
+ dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, ABB_RAMP_VAL(0, 0));
+
+ /* Iota registers values will be programmed at 1st DSP communication interrupt */
+
+ /* Enable f_tx delay of 400000 cyc DEBUG */
+ ndb->d_debug1 = ABB_VAL_T(0, 0x000);
+ ndb->d_afcctladd= ABB_VAL_T(AFCCTLADD, 0x000); // Value at reset
+ ndb->d_vbuctrl = ABB_VAL_T(VBUCTRL, 0x0C9); // Uplink gain amp 0dB, Sidetone gain to mute
+ ndb->d_vbdctrl = ABB_VAL_T(VBDCTRL, 0x006); // Downlink gain amp 0dB, Volume control 0 dB
+ ndb->d_bbctrl = ABB_VAL_T(BBCTRL, 0x2C1); // value at reset
+ ndb->d_bulgcal = ABB_VAL_T(BULGCAL, 0x000); // value at reset
+ ndb->d_apcoff = ABB_VAL_T(APCOFF, 0x040); // value at reset
+ ndb->d_bulioff = ABB_VAL_T(BULIOFF, 0x0FF); // value at reset
+ ndb->d_bulqoff = ABB_VAL_T(BULQOFF, 0x0FF); // value at reset
+ ndb->d_dai_onoff= ABB_VAL_T(APCOFF, 0x000); // value at reset
+ ndb->d_auxdac = ABB_VAL_T(AUXDAC, 0x000); // value at reset
+ ndb->d_vbctrl1 = ABB_VAL_T(VBCTRL1, 0x00B); // VULSWITCH=0, VDLAUX=1, VDLEAR=1.
+ ndb->d_vbctrl2 = ABB_VAL_T(VBCTRL2, 0x000); // MICBIASEL=0, VDLHSO=0, MICAUX=0
+
+ /* APCDEL will be initialized on rach only */
+ ndb->d_apcdel1 = ABB_VAL_T(APCDEL1, ((APCDEL_DOWN-2) << 5) | (APCDEL_UP-6));
+ ndb->d_apcdel2 = ABB_VAL_T(APCDEL2, 0x000);
+
+ ndb->d_fb_mode = 1; /* mode 1 FCCH burst detection */
+ ndb->d_fb_det = 0; /* we have not yet detected a FB */
+ ndb->a_cd[0] = (1<<B_FIRE1); /* CCCH/SACCH downlink */
+ ndb->a_dd_0[0] = 0;
+ ndb->a_dd_0[2] = 0xffff;
+ ndb->a_dd_1[0] = 0;
+ ndb->a_dd_1[2] = 0xffff;
+ ndb->a_du_0[0] = 0;
+ ndb->a_du_0[2] = 0xffff;
+ ndb->a_du_1[0] = 0;
+ ndb->a_du_1[2] = 0xffff;
+ ndb->a_fd[0] = (1<<B_FIRE1);
+ ndb->a_fd[2] = 0xffff;
+ ndb->d_a5mode = 0;
+ ndb->d_tch_mode = 0x0800;
+
+ #define GUARD_BITS 8 // 11 or 9 for TSM30, 7 for Freerunner
+ ndb->d_tch_mode |= (((GUARD_BITS - 4) & 0x000F) << 7); //Bit 7..10: guard bits
+
+ ndb->a_sch26[0] = (1<<B_SCH_CRC);
+
+ /* Interrupt RIF transmit if FIFO <= threshold with threshold == 0 */
+ /* MCM = 1, XRST = 0, CLKX_AUTO=1, TXM=1, NCLK_EN=1, NCLK13_EN=1,
+ * THRESHOLD = 0, DIV_CLK = 0 (13MHz) */
+ ndb->d_spcx_rif = 0x179;
+}
+
+static void dsp_db_init(void)
+{
+ dsp_api_memset((uint16_t *)BASE_API_W_PAGE_0, sizeof(T_DB_MCU_TO_DSP));
+ dsp_api_memset((uint16_t *)BASE_API_W_PAGE_1, sizeof(T_DB_MCU_TO_DSP));
+ dsp_api_memset((uint16_t *)BASE_API_R_PAGE_0, sizeof(T_DB_DSP_TO_MCU));
+ dsp_api_memset((uint16_t *)BASE_API_R_PAGE_1, sizeof(T_DB_DSP_TO_MCU));
+}
+
+void dsp_power_on(void)
+{
+ /* proabaly a good idea to initialize the whole API area to a know value */
+ dsp_api_memset((uint16_t *)BASE_API_RAM, API_SIZE * 2); // size is in words
+
+ dsp_set_params((int16_t *)&dsp_params, sizeof(dsp_params)/2);
+ dsp_ndb_init();
+ dsp_db_init();
+ dsp_api.frame_ctr = 0;
+ dsp_api.r_page = dsp_api.w_page = dsp_api.r_page_used = 0;
+}
+
+/* test for frequency burst detection */
+#define REG_INT_STAT 0xffff1004
+static void wait_for_frame_irq(void)
+{
+ //puts("Waiting for Frame Interrupt");
+ //while (readb(REG_INT_STAT) & 1)
+ while (readb((void *)0xffff1000) & (1<<4))
+ ;// putchar('.');
+ //puts("Done!\n");
+}
+
+void dsp_end_scenario(void)
+{
+ /* FIXME: we don't yet deal with the MISC_TASK */
+
+ /* End the DSP Scenario */
+ dsp_api.ndb->d_dsp_page = B_GSM_TASK | dsp_api.w_page;
+ dsp_api.w_page ^= 1;
+
+ /* Tell TPU to generate a FRAME interrupt to the DSP */
+ tpu_dsp_frameirq_enable();
+ tpu_frame_irq_en(1, 1);
+}
+
+void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc)
+{
+ dsp_api.db_w->d_task_d = task;
+ dsp_api.db_w->d_burst_d = burst_id;
+ dsp_api.db_w->d_ctrl_system |= tsc & 0x7;
+}
+
+void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc)
+{
+ dsp_api.db_w->d_task_u = task;
+ dsp_api.db_w->d_burst_u = burst_id;
+ dsp_api.db_w->d_ctrl_system |= tsc & 0x7;
+}
+
+/* no AMR, no ciphering yet, fn does not work this way */
+void dsp_load_tch_param(uint16_t fn, uint8_t chan_mode, uint8_t chan_type,
+ uint8_t subchannel, uint8_t tch_loop, uint8_t sync_tch)
+{
+ uint16_t d_ctrl_tch;
+
+ /* d_ctrl_tch
+ ----------
+ bit [0..3] -> b_chan_mode
+ bit [4..7] -> b_chan_type
+ bit [8] -> b_sync_tch_ul
+ bit [9] -> b_sync_tch_dl
+ bit [10] -> b_stop_tch_ul
+ bit [11] -> b_stop_tch_dl
+ bit [12..14] -> b_tch_loop
+ bit [15] -> b_subchannel */
+ d_ctrl_tch = (chan_mode << B_CHAN_MODE) |
+ (chan_type << B_CHAN_TYPE) |
+ (subchannel << B_SUBCHANNEL) |
+ (sync_tch << B_SYNC_TCH_UL) |
+ (sync_tch << B_SYNC_TCH_DL) |
+ (tch_loop << B_TCH_LOOP);
+
+ /* TODO (used for ciphering and TCH traffic) */
+
+ /* d_fn
+ ----
+ bit [0..7] -> b_fn_report
+ bit [8..15] -> b_fn_sid */
+ dsp_api.db_w->d_fn = fn; /* write both Fn_sid, Fn_report. */
+ dsp_api.db_w->a_a5fn[0] = 0; /* cyphering FN part 1 (TODO) */
+ dsp_api.db_w->a_a5fn[1] = 0; /* cyphering FN part 2 (TODO) */
+ dsp_api.db_w->d_ctrl_tch = d_ctrl_tch; /* Channel config. */
+}
+
+#define SC_CHKSUM_VER (BASE_API_W_PAGE_0 + (2 * (0x08DB - 0x800)))
+static void dsp_dump_csum(void)
+{
+ printf("dsp page : %u\n", dsp_api.ndb->d_dsp_page);
+ printf("dsp code version : 0x%04x\n", dsp_api.db_r->a_pm[0]);
+ printf("dsp checksum : 0x%04x\n", dsp_api.db_r->a_pm[1]);
+ printf("dsp patch version : 0x%04x\n", readw(SC_CHKSUM_VER));
+}
+
+void dsp_checksum_task(void)
+{
+ dsp_dump_csum();
+ dsp_api.db_w->d_task_md = CHECKSUM_DSP_TASK;
+ dsp_api.ndb->d_fb_mode = 1;
+
+ dsp_end_scenario();
+
+ wait_for_frame_irq();
+
+ dsp_dump_csum();
+}
+
+#define L1D_AUXAPC 0x0012
+#define L1D_APCRAM 0x0014
+
+void dsp_load_apc_dac(uint16_t apc)
+{
+ dsp_api.db_w->d_power_ctl = (apc << 6) | L1D_AUXAPC;
+}
+
+
+static void _dsp_dump_range(uint32_t addr, uint32_t size, int mode)
+{
+ uint32_t bs;
+
+ /* Mode selection */
+ writew(mode, BASE_API_RAM);
+ writew(BL_CMD_COPY_MODE, BL_CMD_STATUS);
+ dsp_bl_wait_ready();
+
+ /* Block by block dump */
+ while (size) {
+ volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM;
+
+ bs = (size > BL_MAX_BLOCK_SIZE) ? BL_MAX_BLOCK_SIZE : size;
+ size -= bs;
+
+ writew(addr >> 16, BL_ADDR_HI);
+ writew(addr & 0xffff, BL_ADDR_LO);
+ writew(bs, BL_SIZE);
+ writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS);
+
+ dsp_bl_wait_ready();
+
+ while (bs--) {
+ if ((addr&15)==0)
+ printf("%05x : ", addr);
+ printf("%04hx%c", *api++, ((addr&15)==15)?'\n':' ');
+ addr++;
+ }
+ };
+ puts("\n");
+}
+
+void dsp_dump(void)
+{
+ static const struct {
+ const char *name;
+ uint32_t addr;
+ uint32_t size;
+ int mode;
+ } dr[] = {
+ { "Registers", 0x00000, 0x0060, BL_MODE_DATA_READ },
+ { "DROM", 0x09000, 0x5000, BL_MODE_DROM_READ },
+ { "PDROM", 0x0e000, 0x2000, BL_MODE_DROM_READ },
+ { "PROM0", 0x07000, 0x7000, BL_MODE_PROM_READ },
+ { "PROM1", 0x18000, 0x8000, BL_MODE_PROM_READ },
+ { "PROM2", 0x28000, 0x8000, BL_MODE_PROM_READ },
+ { "PROM3", 0x38000, 0x2000, BL_MODE_PROM_READ },
+ { NULL, 0, 0, -1 }
+ };
+
+ int i;
+
+ /* Start DSP up to bootloader */
+ dsp_pre_boot(dsp_bootcode);
+
+ /* Load and execute our dump code in the DSP */
+ dsp_upload_sections_api(dsp_dumpcode, DSP_BASE_API);
+
+ writew(0, BL_ADDR_HI);
+ writew(DSP_DUMPCODE_START, BL_ADDR_LO);
+ writew(0, BL_SIZE);
+ writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS);
+
+ /* our dump code actually simulates the boot loaded
+ * but with added read commands */
+ dsp_bl_wait_ready();
+
+ /* Test the 'version' command */
+ writew(0xffff, BL_CMD_STATUS);
+ dsp_bl_wait_ready();
+ printf("DSP bootloader version 0x%04x\n", readw(BASE_API_RAM));
+
+ /* Dump each range */
+ for (i=0; dr[i].name; i++) {
+ printf("DSP dump: %s [%05x-%05x]\n", dr[i].name,
+ dr[i].addr, dr[i].addr+dr[i].size-1);
+ _dsp_dump_range(dr[i].addr, dr[i].size, dr[i].mode);
+ }
+}
+
diff --git a/src/target/firmware/calypso/dsp_bootcode.c b/src/target/firmware/calypso/dsp_bootcode.c
new file mode 100644
index 00000000..2db46568
--- /dev/null
+++ b/src/target/firmware/calypso/dsp_bootcode.c
@@ -0,0 +1,9 @@
+/* Calypso integrated DSP boot code */
+
+#define _SA_DECL (const uint16_t *)&(const uint16_t [])
+
+/* We don't really need any DSP boot code, it happily works with its own ROM */
+static const struct dsp_section *dsp_bootcode = NULL;
+
+#undef _SA_DECL
+
diff --git a/src/target/firmware/calypso/dsp_dumpcode.c b/src/target/firmware/calypso/dsp_dumpcode.c
new file mode 100644
index 00000000..265a1c12
--- /dev/null
+++ b/src/target/firmware/calypso/dsp_dumpcode.c
@@ -0,0 +1,45 @@
+/* Generated from src/target_dsp/calypso/dsp_dump.bin */
+
+#define _SA_DECL (const uint16_t *)&(const uint16_t [])
+
+static const struct dsp_section dsp_dumpcode[] = {
+ {
+ .addr = 0x1000,
+ .size = 0x005b,
+ .data = _SA_DECL {
+ 0x69f8, 0x0029, 0x0002, 0xea1f,
+ 0x7718, 0x1100, 0x7714, 0x0000,
+ 0x7712, 0x0800, 0x767f, 0x0001,
+ 0x607f, 0xffff, 0xf820, 0x1014,
+ 0xf273, 0x1008, 0x7682, 0x0100,
+ 0x607f, 0x0004, 0xf820, 0x101c,
+ 0xf273, 0x1008, 0x7214, 0x0800,
+ 0x607f, 0x0002, 0xf820, 0x100c,
+ 0x127e, 0x8813, 0x3c7c, 0x137d,
+ 0x8911, 0xf84c, 0x1028, 0xf4e2,
+ 0x7715, 0x0014, 0x963d, 0xfa30,
+ 0x104b, 0x6d89, 0x963f, 0xfa30,
+ 0x103f, 0x963e, 0xf495, 0xf830,
+ 0x103a, 0x47f8, 0x0011, 0x7f92,
+ 0xf073, 0x1008, 0x47f8, 0x0011,
+ 0x7e92, 0xf073, 0x1008, 0xf830,
+ 0x1046, 0x47f8, 0x0011, 0xe589,
+ 0xf073, 0x1008, 0x47f8, 0x0011,
+ 0xe598, 0xf073, 0x1008, 0x4911,
+ 0x891a, 0xf830, 0x1055, 0xf072,
+ 0x1052, 0xf074, 0x7213, 0xf073,
+ 0x1008, 0xf072, 0x1058, 0xf074,
+ 0xe4b8, 0xf073, 0x1008,
+ },
+ },
+ { /* Guard */
+ .addr = 0,
+ .size = 0,
+ .data = NULL,
+ },
+};
+
+#define DSP_DUMPCODE_START 0x1000
+
+#undef _SA_DECL
+
diff --git a/src/target/firmware/calypso/dsp_params.c b/src/target/firmware/calypso/dsp_params.c
new file mode 100644
index 00000000..ec44a0ed
--- /dev/null
+++ b/src/target/firmware/calypso/dsp_params.c
@@ -0,0 +1,94 @@
+/* Values from an actual phone firmware that uses the 3306 DSP ROM code version */
+static T_PARAM_MCU_DSP dsp_params = {
+ .d_transfer_rate = 0x6666,
+ /* Latencies */
+ .d_lat_mcu_bridge = 15,
+ .d_lat_mcu_hom2sam = 12,
+ .d_lat_mcu_bef_fast_access = 5,
+ .d_lat_dsp_after_sam = 4,
+ /* DSP Start Address */
+ .d_gprs_install_address = 0x7002, /* needs to be set by patch or manually */
+ .d_misc_config = 1,
+ .d_cn_sw_workaround = 0xE,
+ .d_hole2_param = { 0, 0, 0, 0 },
+ /* Frequency Burst */
+ .d_fb_margin_beg = 24,
+ .d_fb_margin_end = 22,
+ .d_nsubb_idle = 296,
+ .d_nsubb_dedic = 30,
+ .d_fb_thr_det_iacq = 0x3333,
+ .d_fb_thr_det_track = 0x28f6,
+ /* Demodulation */
+ .d_dc_off_thres = 0x7fff,
+ .d_dummy_thres = 17408,
+ .d_dem_pond_gewl = 26624,
+ .d_dem_pond_red = 20152,
+ /* TCH Full Speech */
+ .d_maccthresh1 = 7872,
+ .d_mldt = -4,
+ .d_maccthresh = 7172,
+ .d_gu = 5772,
+ .d_go = 7872,
+ .d_attmax = 53,
+ .d_sm = -892,
+ .d_b = 208,
+ /* V.42 bis */
+ .d_v42b_switch_hyst = 16,
+ .d_v42b_switch_min = 64,
+ .d_v42b_switch_max = 250,
+ .d_v42b_reset_delay = 10,
+ /* TCH Half Speech */
+ .d_ldT_hr = -5,
+ .d_maccthresh_hr = 6500,
+ .d_maccthresh1_hr = 6500,
+ .d_gu_hr = 2620,
+ .d_go_hr = 3700,
+ .d_b_hr = 182,
+ .d_sm_hr = -1608,
+ .d_attmax_hr = 53,
+ /* TCH Enhanced FR Speech */
+ .c_mldt_efr = -4,
+ .c_maccthresh_efr = 8000,
+ .c_maccthresh1_efr = 8000,
+ .c_gu_efr = 4522,
+ .c_go_efr = 6500,
+ .c_b_efr = 174,
+ .c_sm_efr = -878,
+ .c_attmax_efr = 53,
+ /* CHED TCH Full Speech */
+ .d_sd_min_thr_tchfs = 15,
+ .d_ma_min_thr_tchfs = 738,
+ .d_md_max_thr_tchfs = 1700,
+ .d_md1_max_thr_tchfs = 99,
+ /* CHED TCH Half Speech */
+ .d_sd_min_thr_tchhs = 37,
+ .d_ma_min_thr_tchhs = 344,
+ .d_sd_av_thr_tchhs = 1845,
+ .d_md_max_thr_tchhs = 2175,
+ .d_md1_max_thr_tchhs = 138,
+ /* CHED TCH/F EFR Speech */
+ .d_sd_min_thr_tchefs = 15,
+ .d_ma_min_thr_tchefs = 738,
+ .d_md_max_thr_tchefs = 0x4ce,
+ .d_md1_max_thr_tchefs = 0x63,
+ /* */
+ .d_wed_fil_ini = 0x122a,
+ .d_wed_fil_tc = 0x7c00,
+ .d_x_min = 0xf,
+ .d_x_max = 0x17,
+ .d_slope = 0x87,
+ .d_y_min = 0x2bf,
+ .d_y_max = 0x99c,
+ .d_wed_diff_threshold = 0x196,
+ .d_mabfi_min_thr_tchhs = 0x14c8,
+ /* FACCH module */
+ .d_facch_thr = 0,
+ /* IDS module */
+ .d_max_ovsp_ul = 8,
+ .d_sync_thres = 0x3f50,
+ .d_idle_thres = 0x4000,
+ .d_m1_thres = 5,
+ .d_max_ovsp_dl = 8,
+ .d_gsm_bgd_mgt = 0,
+ /* we don't set the FIR coefficients !?! */
+};
diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c
new file mode 100644
index 00000000..58783b06
--- /dev/null
+++ b/src/target/firmware/calypso/du.c
@@ -0,0 +1,51 @@
+/* Calypso DU (Debug Unit) Driver */
+
+/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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 <memory.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <calypso/du.h>
+
+#define BASE_ADDR_DU 0x03c00000
+#define DU_REG(m) (BASE_ADDR_DU+(m))
+
+void calypso_du_init() {
+ unsigned char c;
+ calypso_debugunit(1);
+ for(c = 0; c < 64; c++) {
+ writew(DU_REG(c), 0x00000000);
+ }
+}
+
+void calypso_du_stop() {
+ calypso_debugunit(0);
+}
+
+void calypso_du_dump() {
+ unsigned char c;
+ puts("Debug unit traceback:\n");
+ for(c = 0; c < 64; c++) {
+ uint32_t w = readw(DU_REG(c));
+ printf("t-%2x: 0x%8x\n", c, (unsigned int)w);
+ }
+}
diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c
new file mode 100644
index 00000000..a46fd72a
--- /dev/null
+++ b/src/target/firmware/calypso/i2c.c
@@ -0,0 +1,123 @@
+/* Driver for I2C Master Controller inside TI Calypso */
+
+/* (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 <memory.h>
+#include <i2c.h>
+
+#define BASE_ADDR_I2C 0xfffe2800
+#define I2C_REG(x) (BASE_ADDR_I2C+(x))
+
+enum i2c_reg {
+ DEVICE_REG = 0,
+ ADDRESS_REG,
+ DATA_WR_REG,
+ DATA_RD_REG,
+ CMD_REG,
+ CONF_FIFO_REG,
+ CONF_CLK_REG,
+ CONF_CLK_FUNC_REF,
+ STATUS_FIFO_REG,
+ STATUS_ACTIVITY_REG,
+};
+
+#define I2C_CMD_SOFT_RESET (1 << 0)
+#define I2C_CMD_EN_CLK (1 << 1)
+#define I2C_CMD_START (1 << 2)
+#define I2C_CMD_RW_READ (1 << 3)
+#define I2C_CMD_COMP_READ (1 << 4)
+#define I2C_CMD_IRQ_ENABLE (1 << 5)
+
+#define I2C_STATUS_ERROR_DATA (1 << 0)
+#define I2C_STATUS_ERROR_DEV (1 << 1)
+#define I2C_STATUS_IDLE (1 << 2)
+#define I2C_STATUS_INTERRUPT (1 << 3)
+
+int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len)
+{
+ uint8_t cmd;
+
+ /* Calypso I2C controller doesn't support fancy addressing */
+ if (alen > 1)
+ return -1;
+
+ /* FIXME: implement writes longer than fifo size */
+ if (len > 16)
+ return -1;
+
+ printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr)
+
+ writeb(chip & 0x3f, I2C_REG(DEVICE_REG));
+ writeb(addr & 0xff, I2C_REG(ADDRESS_REG));
+
+ /* we have to tell the controler how many bits we'll put into the fifo ?!? */
+ writeb(len-1, I2C_REG(CONF_FIFO_REG));
+
+ /* fill the FIFO */
+ while (len--) {
+ uint8_t byte = *buffer++;
+ writeb(byte, I2C_REG(DATA_WR_REG));
+ printd("%02X ", byte);
+ }
+ dputchar('\n');
+
+ /* start the transfer */
+ cmd = readb(I2C_REG(CMD_REG));
+ cmd |= I2C_CMD_START;
+ writeb(cmd, I2C_REG(CMD_REG));
+
+ /* wait until transfer completes */
+ while (1) {
+ uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG));
+ printd("I2C Status: 0x%02x\n", rerg & 0xf);
+ if (reg & I2C_STATUS_IDLE)
+ break;
+ }
+ dputs("I2C transfer completed\n");
+
+ return 0;
+}
+
+void i2c_init(int speed, int slaveadd)
+{
+ /* scl_out = clk_func_ref / 3,
+ clk_func_ref = master_clock_freq / (divisor_2 + 1)
+ master_clock_freq = ext_clock_freq / divisor_1 */
+ /* clk_func_ref = scl_out * 3,
+ divisor_2 = (master_clock_freq / clk_func_ref) - 1
+ divisor_1 = ext_clock_freq / master_clock_freq */
+ /* for a target freq of 200kHz:
+ ext_clock_freq = 13MHz
+ clk_func_ref = 3 * 300kHZ = 600kHz
+ divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz
+ divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz
+ scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */
+ writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG));
+
+ writeb(0x00, I2C_REG(CONF_CLK_REG));
+ writeb(21, I2C_REG(CONF_CLK_FUNC_REF));
+
+ writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG));
+}
diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c
new file mode 100644
index 00000000..a3d57fbe
--- /dev/null
+++ b/src/target/firmware/calypso/irq.c
@@ -0,0 +1,266 @@
+/* Driver for Calypso IRQ controller */
+
+/* (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 <memory.h>
+#include <arm.h>
+#include <calypso/irq.h>
+
+#define BASE_ADDR_IRQ 0xfffffa00
+
+enum irq_reg {
+ IT_REG1 = 0x00,
+ IT_REG2 = 0x02,
+ MASK_IT_REG1 = 0x08,
+ MASK_IT_REG2 = 0x0a,
+ IRQ_NUM = 0x10,
+ FIQ_NUM = 0x12,
+ IRQ_CTRL = 0x14,
+};
+
+#define ILR_IRQ(x) (0x20 + (x*2))
+#define IRQ_REG(x) ((void *)BASE_ADDR_IRQ + (x))
+
+#define NR_IRQS 32
+
+static uint8_t default_irq_prio[] = {
+ [IRQ_WATCHDOG] = 0xff,
+ [IRQ_TIMER1] = 0xff,
+ [IRQ_TIMER2] = 0xff,
+ [IRQ_TSP_RX] = 0,
+ [IRQ_TPU_FRAME] = 3,
+ [IRQ_TPU_PAGE] = 0xff,
+ [IRQ_SIMCARD] = 0xff,
+ [IRQ_UART_MODEM] = 8,
+ [IRQ_KEYPAD_GPIO] = 4,
+ [IRQ_RTC_TIMER] = 9,
+ [IRQ_RTC_ALARM_I2C] = 10,
+ [IRQ_ULPD_GAUGING] = 2,
+ [IRQ_EXTERNAL] = 12,
+ [IRQ_SPI] = 0xff,
+ [IRQ_DMA] = 0xff,
+ [IRQ_API] = 0xff,
+ [IRQ_SIM_DETECT] = 0,
+ [IRQ_EXTERNAL_FIQ] = 7,
+ [IRQ_UART_IRDA] = 2,
+ [IRQ_ULPD_GSM_TIMER] = 1,
+ [IRQ_GEA] = 0xff,
+};
+
+static irq_handler *irq_handlers[NR_IRQS];
+
+static void _irq_enable(enum irq_nr nr, int enable)
+{
+ uint16_t *reg = IRQ_REG(MASK_IT_REG1);
+ uint16_t val;
+
+ if (nr > 15) {
+ reg = IRQ_REG(MASK_IT_REG2);
+ nr -= 16;
+ }
+
+ val = readw(reg);
+ if (enable)
+ val &= ~(1 << nr);
+ else
+ val |= (1 << nr);
+ writew(val, reg);
+}
+
+void irq_enable(enum irq_nr nr)
+{
+ _irq_enable(nr, 1);
+}
+
+void irq_disable(enum irq_nr nr)
+{
+ _irq_enable(nr, 0);
+}
+
+void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio)
+{
+ uint16_t val;
+
+ if (prio == -1)
+ prio = default_irq_prio[nr];
+
+ if (prio > 31)
+ prio = 31;
+
+ val = prio << 2;
+ if (edge)
+ val |= 0x02;
+ if (fiq)
+ val |= 0x01;
+
+ writew(val, IRQ_REG(ILR_IRQ(nr)));
+}
+
+/* Entry point for interrupts */
+void irq(void)
+{
+ uint8_t num, tmp;
+ irq_handler *handler;
+
+#if 1
+ /* Hardware interrupt detection mode */
+ num = readb(IRQ_REG(IRQ_NUM)) & 0x1f;
+
+ printd("i%02x\n", num);
+
+ handler = irq_handlers[num];
+
+ if (handler)
+ handler(num);
+#else
+ /* Software interrupt detection mode */
+ {
+ uint16_t it_reg, mask_reg;
+ uint32_t irqs;
+
+ it_reg = readw(IRQ_REG(IT_REG1));
+ mask_reg = readw(IRQ_REG(MASK_IT_REG1));
+ irqs = it_reg & ~mask_reg;
+
+ it_reg = readw(IRQ_REG(IT_REG2));
+ mask_reg = readw(IRQ_REG(MASK_IT_REG2));
+ irqs |= (it_reg & ~mask_reg) << 16;
+
+ for (num = 0; num < 32; num++) {
+ if (irqs & (1 << num)) {
+ printd("i%d\n", num);
+ handler = irq_handlers[num];
+ if (handler)
+ handler(num);
+ /* clear this interrupt */
+ if (num < 16)
+ writew(~(1 << num), IRQ_REG(IT_REG1));
+ else
+ writew(~(1 << (num-16)), IRQ_REG(IT_REG2));
+ }
+ }
+ dputchar('\n');
+ }
+#endif
+ /* Start new IRQ agreement */
+ tmp = readb(IRQ_REG(IRQ_CTRL));
+ tmp |= 0x01;
+ writeb(tmp, IRQ_REG(IRQ_CTRL));
+}
+
+/* Entry point for FIQs */
+void fiq(void)
+{
+ uint8_t num, tmp;
+ irq_handler *handler;
+
+ num = readb(IRQ_REG(FIQ_NUM)) & 0x1f;
+ if (num) {
+ printd("f%02x\n", num);
+ }
+
+ handler = irq_handlers[num];
+
+ if (handler)
+ handler(num);
+
+ /* Start new FIQ agreement */
+ tmp = readb(IRQ_REG(IRQ_CTRL));
+ tmp |= 0x02;
+ writeb(tmp, IRQ_REG(IRQ_CTRL));
+}
+
+void irq_register_handler(enum irq_nr nr, irq_handler *handler)
+{
+ if (nr > NR_IRQS)
+ return;
+
+ irq_handlers[nr] = handler;
+}
+
+#define BASE_ADDR_IBOOT_EXC 0x0080001C
+extern uint32_t _exceptions;
+
+/* Install the exception handlers to where the ROM loader jumps */
+void calypso_exceptions_install(void)
+{
+ uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC;
+ uint32_t *exceptions_src = &_exceptions;
+ int i;
+
+ for (i = 0; i < 7; i++)
+ *exceptions_dst++ = *exceptions_src++;
+
+}
+
+static void set_default_priorities(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) {
+ uint16_t val;
+ uint8_t prio = default_irq_prio[i];
+ if (prio > 31)
+ prio = 31;
+
+ val = readw(IRQ_REG(ILR_IRQ(i)));
+ val &= ~(0x1f << 2);
+ val |= prio << 2;
+ writew(val, IRQ_REG(ILR_IRQ(i)));
+ }
+}
+
+static uint32_t irq_nest_mask;
+/* mask off all interrupts that have a lower priority than irq_nr */
+static void mask_all_lower_prio_irqs(enum irq_nr irqnr)
+{
+ uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irqnr))) >> 2;
+ int i;
+
+ for (i = 0; i < _NR_IRQ; i++) {
+ uint8_t prio;
+
+ if (i == irqnr)
+ continue;
+
+ prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2;
+ if (prio >= our_prio)
+ irq_nest_mask |= (1 << i);
+ }
+}
+
+void irq_init(void)
+{
+ /* set default priorities */
+ set_default_priorities();
+ /* mask all interrupts off */
+ writew(0xffff, IRQ_REG(MASK_IT_REG1));
+ writew(0xffff, IRQ_REG(MASK_IT_REG2));
+ /* clear all pending interrupts */
+ writew(0, IRQ_REG(IT_REG1));
+ writew(0, IRQ_REG(IT_REG2));
+ /* enable interrupts globally to the ARM core */
+ arm_enable_interrupts();
+}
diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c
new file mode 100644
index 00000000..8d9c9251
--- /dev/null
+++ b/src/target/firmware/calypso/keypad.c
@@ -0,0 +1,168 @@
+/* Driver for the keypad attached to the TI Calypso */
+
+/* (C) 2010 by roh <roh@hyte.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 <defines.h>
+#include <debug.h>
+#include <delay.h>
+#include <memory.h>
+#include <keypad.h>
+
+#include <calypso/irq.h>
+#include <abb/twl3025.h>
+
+
+#define KBR_LATCH_REG 0xfffe480a
+#define KBC_REG 0xfffe480c
+#define KBD_GPIO_INT 0xfffe4816
+#define KBD_GPIO_MASKIT 0xfffe4818
+
+static key_handler_t key_handler = NULL;
+
+void emit_key(uint8_t key, uint8_t state)
+{
+ printf("key=%u %s\n", key, state == PRESSED ? "pressed" : "released");
+
+ if (state == RELEASED)
+ if (key == KEY_POWER)
+ twl3025_power_off();
+
+ if(key_handler) {
+ key_handler(key, state);
+ }
+}
+
+volatile uint32_t lastbuttons;
+
+#define BTN_TO_KEY(name) \
+ ((diff & BTN_##name) == BTN_##name) \
+ { \
+ key = KEY_##name; \
+ diff = diff & ~BTN_##name; \
+ }
+
+void dispatch_buttons(uint32_t buttons)
+{
+ uint8_t state;
+
+ if (buttons == lastbuttons)
+ return;
+
+ if (buttons > lastbuttons)
+ state = PRESSED;
+ else
+ state = RELEASED;
+
+ uint32_t diff = buttons ^ lastbuttons;
+ uint8_t key=KEY_INV;
+
+ while (diff != 0)
+ {
+ if BTN_TO_KEY(POWER)
+ else if BTN_TO_KEY(0)
+ else if BTN_TO_KEY(1)
+ else if BTN_TO_KEY(2)
+ else if BTN_TO_KEY(3)
+ else if BTN_TO_KEY(4)
+ else if BTN_TO_KEY(5)
+ else if BTN_TO_KEY(6)
+ else if BTN_TO_KEY(7)
+ else if BTN_TO_KEY(8)
+ else if BTN_TO_KEY(9)
+ else if BTN_TO_KEY(STAR)
+ else if BTN_TO_KEY(HASH)
+ else if BTN_TO_KEY(MENU)
+ else if BTN_TO_KEY(LEFT_SB)
+ else if BTN_TO_KEY(RIGHT_SB)
+ else if BTN_TO_KEY(UP)
+ else if BTN_TO_KEY(DOWN)
+ else if BTN_TO_KEY(LEFT)
+ else if BTN_TO_KEY(RIGHT)
+ else if BTN_TO_KEY(OK)
+ else
+ {
+ printf("\nunknown keycode: 0x%08x\n", diff);
+ break;
+ }
+ if ( key == KEY_POWER )
+ diff = 0;
+ emit_key(key, state);
+ }
+ lastbuttons = buttons;
+}
+
+static void keypad_irq(__unused enum irq_nr nr)
+{
+ keypad_poll();
+}
+
+void keypad_init(uint8_t interrupts)
+{
+ lastbuttons = 0;
+ writew(0, KBD_GPIO_MASKIT);
+ writew(0, KBC_REG);
+
+ if(interrupts) {
+ irq_register_handler(IRQ_KEYPAD_GPIO, &keypad_irq);
+ irq_config(IRQ_KEYPAD_GPIO, 0, 0, 0);
+ irq_enable(IRQ_KEYPAD_GPIO);
+ }
+}
+
+void keypad_set_handler(key_handler_t handler)
+{
+ key_handler = handler;
+}
+
+void keypad_poll()
+{
+ uint16_t reg;
+ uint16_t col;
+ uint32_t buttons;
+
+// putchar('\n');
+ buttons = 0x0;
+ //scan for BTN_POWER
+ writew(0xff, KBC_REG);
+ delay_ms(1);
+ reg = readw(KBR_LATCH_REG);
+// printd("%02x ", (~reg & 0x1f));
+ buttons = buttons | ((~reg & 0x1f) << 20 );
+
+ //scan for muxed keys if not powerbtn
+ if ((~reg & 0x1f) != 0x10)
+ for (col=0;col<4;col++)
+ {
+ writew(0x1f & ~(0x1 << col ), KBC_REG);
+ delay_ms(1);
+ reg = readw(KBR_LATCH_REG);
+ buttons = buttons | ((~reg & 0x1f) << (col * 5 ));
+// printd("%02x ", (~reg & 0x1f));
+ }
+ //enable keypad irq via master 'or' gate (needs col lines low in idle to work)
+ writew(0, KBC_REG);
+ dispatch_buttons(buttons);
+
+}
+
diff --git a/src/target/firmware/calypso/misc.c b/src/target/firmware/calypso/misc.c
new file mode 100644
index 00000000..460cc5d5
--- /dev/null
+++ b/src/target/firmware/calypso/misc.c
@@ -0,0 +1,60 @@
+
+#include <stdint.h>
+#include <stdio.h>
+#include <memory.h>
+
+/* dump a memory range */
+void memdump_range(unsigned int *ptr, unsigned int len)
+{
+ unsigned int *end = ptr + (len/4);
+ unsigned int *tmp;
+
+ for (tmp = ptr; tmp < end; tmp += 8) {
+ int i;
+ printf("%08X: ", (unsigned int) tmp);
+
+ for (i = 0; i < 8; i++)
+ printf("%08X %s", *(tmp+i), i == 3 ? " " : "");
+
+ putchar('\n');
+ }
+}
+
+#define KBIT 1024
+#define MBIT (1024*KBIT)
+void dump_mem(void)
+{
+ puts("Dump 64kBits of internal ROM\n");
+ memdump_range((void *)0x03800000, 64*KBIT/8);
+
+ puts("Dump 8Mbits of external flash\n");
+ memdump_range((void *)0x00000000, 8*MBIT/8);
+
+ puts("Dump 2Mbits of internal RAM\n");
+ memdump_range((void *)0x00800000, 2*MBIT/8);
+
+ puts("Dump 2Mbits of external RAM\n");
+ memdump_range((void *)0x01000000, 2*MBIT/8);
+}
+
+#define REG_DEV_ID_CODE 0xfffef000
+#define REG_DEV_VER_CODE 0xfffef002
+#define REG_DEV_ARMVER_CODE 0xfffffe00
+#define REG_cDSP_ID_CODE 0xfffffe02
+#define REG_DIE_ID_CODE 0xfffef010
+
+void dump_dev_id(void)
+{
+ int i;
+
+ printf("Device ID code: 0x%04x\n", readw(REG_DEV_ID_CODE));
+ printf("Device Version code: 0x%04x\n", readw(REG_DEV_VER_CODE));
+ printf("ARM ID code: 0x%04x\n", readw(REG_DEV_ARMVER_CODE));
+ printf("cDSP ID code: 0x%04x\n", readw(REG_cDSP_ID_CODE));
+ puts("Die ID code: ");
+ for (i = 0; i < 64/8; i += 4)
+ printf("%08x", readl(REG_DIE_ID_CODE+i));
+ putchar('\n');
+}
+
+
diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c
new file mode 100644
index 00000000..ce750c29
--- /dev/null
+++ b/src/target/firmware/calypso/rtc.c
@@ -0,0 +1,83 @@
+/* Driver for Calypso RTC controller */
+
+/* (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 <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <display.h>
+#include <calypso/irq.h>
+
+#define BASE_ADDR_RTC 0xfffe1800
+#define RTC_REG(x) ((void *)BASE_ADDR_RTC + (x))
+
+enum rtc_reg {
+ SECOND_REG = 0x00,
+ MINUTES_REG = 0x01,
+ HOURS_REG = 0x02,
+ DAYS_REG = 0x03,
+ MONTHS_REG = 0x04,
+ YEARS_REG = 0x05,
+ WEEK_REG = 0x06,
+ /* reserved */
+ ALARM_SECOND_REG = 0x08,
+ ALARM_MINUTES_REG = 0x09,
+ ALARM_HOURS_REG = 0x0a,
+ ALARM_DAYS_REG = 0x0b,
+ ALARM_MONTHS_REG = 0x0c,
+ ALARM_YEARS_REG = 0x0d,
+ /* reserved */
+ /* reserved */
+ CTRL_REG = 0x10,
+ STATUS_REG = 0x11,
+ INT_REG = 0x12,
+ COMP_LSB_REG = 0x13,
+ COMP_MSB_REG = 0x14,
+ RES_PROG_REG = 0x15,
+};
+
+static int tick_ctr;
+
+static void rtc_irq_tick(__unused enum irq_nr nr)
+{
+ if (tick_ctr & 1)
+ display_set_attr(DISP_ATTR_INVERT);
+ else
+ display_unset_attr(DISP_ATTR_INVERT);
+ tick_ctr++;
+}
+
+void rtc_init(void)
+{
+ irq_register_handler(IRQ_RTC_TIMER, &rtc_irq_tick);
+ irq_config(IRQ_RTC_TIMER, 0, 1, 0);
+ irq_enable(IRQ_RTC_TIMER);
+
+ /* clear power-up reset */
+ writeb(0x80, RTC_REG(STATUS_REG));
+ /* enable RTC running */
+ writeb(0x01, RTC_REG(CTRL_REG));
+ /* enable periodic interrupts every second */
+ writeb(0x04, RTC_REG(INT_REG));
+}
diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c
new file mode 100644
index 00000000..049ac080
--- /dev/null
+++ b/src/target/firmware/calypso/spi.c
@@ -0,0 +1,141 @@
+/* Driver for SPI Master Controller inside TI Calypso */
+
+/* (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>
+
+//#define DEBUG
+#include <debug.h>
+
+#include <memory.h>
+#include <spi.h>
+#include <delay.h>
+
+#define BASE_ADDR_SPI 0xfffe3000
+#define SPI_REG(n) (BASE_ADDR_SPI+(n))
+
+enum spi_regs {
+ REG_SET1 = 0x00,
+ REG_SET2 = 0x02,
+ REG_CTRL = 0x04,
+ REG_STATUS = 0x06,
+ REG_TX_LSB = 0x08,
+ REG_TX_MSB = 0x0a,
+ REG_RX_LSB = 0x0c,
+ REG_RX_MSB = 0x0e,
+};
+
+#define SPI_SET1_EN_CLK (1 << 0)
+#define SPI_SET1_WR_IRQ_DIS (1 << 4)
+#define SPI_SET1_RDWR_IRQ_DIS (1 << 5)
+
+#define SPI_CTRL_RDWR (1 << 0)
+#define SPI_CTRL_WR (1 << 1)
+#define SPI_CTRL_NB_SHIFT 2
+#define SPI_CTRL_AD_SHIFT 7
+
+#define SPI_STATUS_RE (1 << 0) /* Read End */
+#define SPI_STATUS_WE (1 << 1) /* Write End */
+
+void spi_init(void)
+{
+ writew(SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS | SPI_SET1_RDWR_IRQ_DIS,
+ SPI_REG(REG_SET1));
+
+ writew(0x0001, SPI_REG(REG_SET2));
+}
+
+int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din)
+{
+ uint8_t bytes_per_xfer;
+ uint8_t reg_status, reg_ctrl = 0;
+ uint32_t tmp;
+
+ if (bitlen == 0)
+ return 0;
+
+ if (bitlen > 32)
+ return -1;
+
+ if (dev_idx > 4)
+ return -1;
+
+ bytes_per_xfer = bitlen / 8;
+ if (bitlen % 8)
+ bytes_per_xfer ++;
+
+ reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT;
+ reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT;
+
+ if (bitlen <= 8) {
+ tmp = *(uint8_t *)dout;
+ tmp <<= 24 + (8-bitlen); /* align to MSB */
+ } else if (bitlen <= 16) {
+ tmp = *(uint16_t *)dout;
+ tmp <<= 16 + (16-bitlen); /* align to MSB */
+ } else {
+ tmp = *(uint32_t *)dout;
+ tmp <<= (32-bitlen); /* align to MSB */
+ }
+ printd("spi_xfer(dev_idx=%u, bitlen=%u, data_out=0x%08x): ",
+ dev_idx, bitlen, tmp);
+
+ /* fill transmit registers */
+ writew(tmp >> 16, SPI_REG(REG_TX_MSB));
+ writew(tmp & 0xffff, SPI_REG(REG_TX_LSB));
+
+ /* initiate transfer */
+ if (din)
+ reg_ctrl |= SPI_CTRL_RDWR;
+ else
+ reg_ctrl |= SPI_CTRL_WR;
+ writew(reg_ctrl, SPI_REG(REG_CTRL));
+ printd("reg_ctrl=0x%04x ", reg_ctrl);
+
+ /* wait until the transfer is complete */
+ while (1) {
+ reg_status = readw(SPI_REG(REG_STATUS));
+ printd("status=0x%04x ", reg_status);
+ if (din && (reg_status & SPI_STATUS_RE))
+ break;
+ else if (reg_status & SPI_STATUS_WE)
+ break;
+ }
+ /* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */
+ delay_ms(1);
+
+ if (din) {
+ tmp = readw(SPI_REG(REG_RX_MSB)) << 16;
+ tmp |= readw(SPI_REG(REG_RX_LSB));
+ printd("data_in=0x%08x ", tmp);
+
+ if (bitlen <= 8)
+ *(uint8_t *)din = tmp & 0xff;
+ else if (bitlen <= 16)
+ *(uint16_t *)din = tmp & 0xffff;
+ else
+ *(uint32_t *)din = tmp;
+ }
+ dputchar('\n');
+
+ return 0;
+}
diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c
new file mode 100644
index 00000000..1dd55f26
--- /dev/null
+++ b/src/target/firmware/calypso/timer.c
@@ -0,0 +1,156 @@
+/* Calypso DBB internal Timer Driver */
+
+/* (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 <stdio.h>
+#include <memory.h>
+#include <stdint.h>
+
+#include <defines.h>
+
+#include <calypso/timer.h>
+#include <calypso/irq.h>
+
+#define BASE_ADDR_TIMER 0xfffe3800
+#define TIMER2_OFFSET 0x3000
+
+#define TIMER_REG(n, m) (((n)-1) ? (BASE_ADDR_TIMER + TIMER2_OFFSET + (m)) : (BASE_ADDR_TIMER + (m)))
+
+enum timer_reg {
+ CNTL_TIMER = 0x00,
+ LOAD_TIMER = 0x02,
+ READ_TIMER = 0x04,
+};
+
+enum timer_ctl {
+ CNTL_START = (1 << 0),
+ CNTL_AUTO_RELOAD = (1 << 1),
+ CNTL_CLOCK_ENABLE = (1 << 5),
+};
+
+/* Regular Timers (1 and 2) */
+
+void hwtimer_enable(int num, int on)
+{
+ uint8_t ctl;
+
+ if (num < 1 || num > 2) {
+ printf("Unknown timer %u\n", num);
+ return;
+ }
+
+ ctl = readb(TIMER_REG(num, CNTL_TIMER));
+ if (on)
+ ctl |= CNTL_START|CNTL_CLOCK_ENABLE;
+ else
+ ctl &= ~CNTL_START;
+ writeb(ctl, TIMER_REG(num, CNTL_TIMER));
+}
+
+void hwtimer_config(int num, uint8_t pre_scale, int auto_reload)
+{
+ uint8_t ctl;
+
+ ctl = (pre_scale & 0x7) << 2;
+ if (auto_reload)
+ ctl |= CNTL_AUTO_RELOAD;
+
+ writeb(ctl, TIMER_REG(num, CNTL_TIMER));
+}
+
+void hwtimer_load(int num, uint16_t val)
+{
+ writew(val, TIMER_REG(num, LOAD_TIMER));
+}
+
+uint16_t hwtimer_read(int num)
+{
+ uint8_t ctl = readb(TIMER_REG(num, CNTL_TIMER));
+
+ /* somehow a read results in an abort */
+ if ((ctl & (CNTL_START|CNTL_CLOCK_ENABLE)) != (CNTL_START|CNTL_CLOCK_ENABLE))
+ return 0xFFFF;
+ return readw(TIMER_REG(num, READ_TIMER));
+}
+
+void hwtimer_init(void)
+{
+ writeb(CNTL_CLOCK_ENABLE, TIMER_REG(1, CNTL_TIMER));
+ writeb(CNTL_CLOCK_ENABLE, TIMER_REG(2, CNTL_TIMER));
+}
+
+/* Watchdog Timer */
+
+#define BASE_ADDR_WDOG 0xfffff800
+#define WDOG_REG(m) (BASE_ADDR_WDOG + m)
+
+enum wdog_reg {
+ WD_CNTL_TIMER = CNTL_TIMER,
+ WD_LOAD_TIMER = LOAD_TIMER,
+ WD_READ_TIMER = 0x02,
+ WD_MODE = 0x04,
+};
+
+enum wdog_ctl {
+ WD_CTL_START = (1 << 7),
+ WD_CTL_AUTO_RELOAD = (1 << 8)
+};
+
+enum wdog_mode {
+ WD_MODE_DIS_ARM = 0xF5,
+ WD_MODE_DIS_CONFIRM = 0xA0,
+ WD_MODE_ENABLE = (1 << 15)
+};
+
+#define WD_CTL_PRESCALE(value) (((value)&0x07) << 9)
+
+static void wdog_irq(__unused enum irq_nr nr)
+{
+ puts("=> WATCHDOG\n");
+}
+
+void wdog_enable(int on)
+{
+ if (on) {
+ irq_config(IRQ_WATCHDOG, 0, 0, 0);
+ irq_register_handler(IRQ_WATCHDOG, &wdog_irq);
+ irq_enable(IRQ_WATCHDOG);
+ writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE));
+ } else {
+ writew(WD_MODE_DIS_ARM, WDOG_REG(WD_MODE));
+ writew(WD_MODE_DIS_CONFIRM, WDOG_REG(WD_MODE));
+ }
+}
+
+void wdog_reset(void)
+{
+#if 0
+ // XXX: this is supposed to reset immediately but does not seem to
+ writew(0xF5, WDOG_REG(WD_MODE));
+ writew(0xFF, WDOG_REG(WD_MODE));
+#else
+ // enable watchdog
+ writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE));
+ // force expiration
+ writew(0x0000, WDOG_REG(WD_LOAD_TIMER));
+ writew(0x0000, WDOG_REG(WD_LOAD_TIMER));
+#endif
+}
diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c
new file mode 100644
index 00000000..ed7aea74
--- /dev/null
+++ b/src/target/firmware/calypso/tpu.c
@@ -0,0 +1,346 @@
+/* Calypso DBB internal TPU (Time Processing Unit) Driver */
+
+/* (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 <delay.h>
+#include <memory.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+
+/* Using TPU_DEBUG you will send special HLDC messages to the host PC
+ * containing the full TPU RAM content at the time you call tpu_enable() */
+//#define TPU_DEBUG
+
+#define BASE_ADDR_TPU 0xffff1000
+#define TPU_REG(x) (BASE_ADDR_TPU+(x))
+
+#define BASE_ADDR_TPU_RAM 0xffff9000
+#define TPU_RAM_END 0xffff97ff
+
+enum tpu_reg_arm {
+ TPU_CTRL = 0x0, /* Control & Status Register */
+ INT_CTRL = 0x2, /* Interrupt Control Register */
+ INT_STAT = 0x4, /* Interrupt Status Register */
+ TPU_OFFSET = 0xC, /* Offset operand value register */
+ TPU_SYNCHRO = 0xE, /* synchro operand value register */
+ IT_DSP_PG = 0x20,
+};
+
+enum tpu_ctrl_bits {
+ TPU_CTRL_RESET = (1 << 0),
+ TPU_CTRL_PAGE = (1 << 1),
+ TPU_CTRL_EN = (1 << 2),
+ /* unused */
+ TPU_CTRL_DSP_EN = (1 << 4),
+ /* unused */
+ TPU_CTRL_MCU_RAM_ACC = (1 << 6),
+ TPU_CTRL_TSP_RESET = (1 << 7),
+ TPU_CTRL_IDLE = (1 << 8),
+ TPU_CTRL_WAIT = (1 << 9),
+ TPU_CTRL_CK_ENABLE = (1 << 10),
+ TPU_CTRL_FULL_WRITE = (1 << 11),
+};
+
+enum tpu_int_ctrl_bits {
+ ICTRL_MCU_FRAME = (1 << 0),
+ ICTRL_MCU_PAGE = (1 << 1),
+ ICTRL_DSP_FRAME = (1 << 2),
+ ICTRL_DSP_FRAME_FORCE = (1 << 3),
+};
+
+static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM;
+
+#ifdef TPU_DEBUG
+#include <comm/sercomm.h>
+#include <layer1/sync.h>
+static void tpu_ram_read_en(int enable)
+{
+ uint16_t reg;
+
+ reg = readw(TPU_REG(TPU_CTRL));
+ if (enable)
+ reg |= TPU_CTRL_MCU_RAM_ACC;
+ else
+ reg &= ~TPU_CTRL_MCU_RAM_ACC;
+ writew(reg, TPU_REG(TPU_CTRL));
+}
+
+static void tpu_debug(void)
+{
+ uint16_t *tpu_base = (uint16_t *)BASE_ADDR_TPU_RAM;
+ unsigned int tpu_size = tpu_ptr - tpu_base;
+ struct msgb *msg = sercomm_alloc_msgb(tpu_size*2);
+ uint16_t *data;
+ uint32_t *fn;
+ uint16_t reg;
+ int i;
+
+ /* prepend tpu memory dump with frame number */
+ fn = (uint32_t *) msgb_put(msg, sizeof(fn));
+ *fn = l1s.current_time.fn;
+
+ tpu_ram_read_en(1);
+
+ data = (uint16_t *) msgb_put(msg, tpu_size*2);
+ for (i = 0; i < tpu_size; i ++)
+ data[i] = tpu_base[i];
+
+ tpu_ram_read_en(0);
+
+ sercomm_sendmsg(SC_DLCI_DEBUG, msg);
+}
+#else
+static void tpu_debug(void) { }
+#endif
+
+#define BIT_SET 1
+#define BIT_CLEAR 0
+
+/* wait for a certain control bit to be set */
+static int tpu_wait_ctrl_bit(uint16_t bit, int set)
+{
+ int timeout = 10*1000;
+
+ while (1) {
+ uint16_t reg = readw(TPU_REG(TPU_CTRL));
+ if (set) {
+ if (reg & bit)
+ break;
+ } else {
+ if (!(reg & bit))
+ break;
+ }
+ timeout--;
+ if (timeout <= 0) {
+ puts("Timeout while waiting for TPU ctrl bit!\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* assert or de-assert TPU reset */
+void tpu_reset(int active)
+{
+ uint16_t reg;
+
+ printd("tpu_reset(%u)\n", active);
+ reg = readw(TPU_REG(TPU_CTRL));
+ if (active) {
+ reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
+ writew(reg, TPU_REG(TPU_CTRL));
+ tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET);
+ } else {
+ reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
+ writew(reg, TPU_REG(TPU_CTRL));
+ tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR);
+ }
+}
+
+/* Enable or Disable a new scenario loaded into the TPU */
+void tpu_enable(int active)
+{
+ uint16_t reg = readw(TPU_REG(TPU_CTRL));
+
+ printd("tpu_enable(%u)\n", active);
+
+ tpu_debug();
+
+ if (active)
+ reg |= TPU_CTRL_EN;
+ else
+ reg &= ~TPU_CTRL_EN;
+ writew(reg, TPU_REG(TPU_CTRL));
+
+ /* After the new scenario is loaded, TPU switches the MCU-visible memory
+ * page, i.e. we can write without any danger */
+ tpu_rewind();
+#if 0
+ {
+ int i;
+ uint16_t oldreg = 0;
+
+ for (i = 0; i < 100000; i++) {
+ reg = readw(TPU_REG(TPU_CTRL));
+ if (i == 0 || oldreg != reg) {
+ printd("%d TPU state: 0x%04x\n", i, reg);
+ }
+ oldreg = reg;
+ }
+ }
+#endif
+}
+
+/* Enable or Disable the clock of teh TPU Module */
+void tpu_clk_enable(int active)
+{
+ uint16_t reg = readw(TPU_REG(TPU_CTRL));
+
+ printd("tpu_clk_enable(%u)\n", active);
+ if (active) {
+ reg |= TPU_CTRL_CK_ENABLE;
+ writew(reg, TPU_REG(TPU_CTRL));
+ tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET);
+ } else {
+ reg &= ~TPU_CTRL_CK_ENABLE;
+ writew(reg, TPU_REG(TPU_CTRL));
+ tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR);
+ }
+}
+
+/* Enable Frame Interrupt generation on next frame. DSP will reset it */
+void tpu_dsp_frameirq_enable(void)
+{
+ uint16_t reg = readw(TPU_REG(TPU_CTRL));
+ reg |= TPU_CTRL_DSP_EN;
+ writew(reg, TPU_REG(TPU_CTRL));
+
+ tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET);
+}
+
+/* Is a Frame interrupt still pending for the DSP ? */
+int tpu_dsp_fameirq_pending(void)
+{
+ uint16_t reg = readw(TPU_REG(TPU_CTRL));
+
+ if (reg & TPU_CTRL_DSP_EN)
+ return 1;
+
+ return 0;
+}
+
+void tpu_rewind(void)
+{
+ dputs("tpu_rewind()\n");
+ tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM;
+}
+
+void tpu_enqueue(uint16_t instr)
+{
+ printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr);
+ *tpu_ptr++ = instr;
+ if (tpu_ptr > (uint16_t *) TPU_RAM_END)
+ puts("TPU enqueue beyond end of TPU memory\n");
+}
+
+void tpu_init(void)
+{
+ uint16_t *ptr;
+
+ /* Put TPU into Reset and enable clock */
+ tpu_reset(1);
+ tpu_clk_enable(1);
+
+ /* set all TPU RAM to zero */
+ for (ptr = (uint16_t *) BASE_ADDR_TPU_RAM; ptr < (uint16_t *) TPU_RAM_END; ptr++)
+ *ptr = 0x0000;
+
+ /* Get TPU out of reset */
+ tpu_reset(0);
+ /* Disable all interrupts */
+ writeb(0x7, TPU_REG(INT_CTRL));
+
+ tpu_rewind();
+ tpu_enq_offset(0);
+ tpu_enq_sync(0);
+}
+
+void tpu_test(void)
+{
+ int i;
+
+ /* program a sequence of TSPACT events into the TPU */
+ for (i = 0; i < 10; i++) {
+ puts("TSP ACT enable: ");
+ tsp_act_enable(0x0001);
+ tpu_enq_wait(10);
+ puts("TSP ACT disable: ");
+ tsp_act_disable(0x0001);
+ tpu_enq_wait(10);
+ }
+ tpu_enq_sleep();
+
+ /* tell the chip to execute the scenario */
+ tpu_enable(1);
+}
+
+void tpu_wait_idle(void)
+{
+ dputs("Waiting for TPU Idle ");
+ /* Wait until TPU is doing something */
+ delay_us(3);
+ /* Wait until TPU is idle */
+ while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE)
+ dputchar('.');
+ dputs("Done!\n");
+}
+
+void tpu_frame_irq_en(int mcu, int dsp)
+{
+ uint8_t reg = readb(TPU_REG(INT_CTRL));
+ if (mcu)
+ reg &= ~ICTRL_MCU_FRAME;
+ else
+ reg |= ICTRL_MCU_FRAME;
+
+ if (dsp)
+ reg &= ~ICTRL_DSP_FRAME;
+ else
+ reg |= ICTRL_DSP_FRAME;
+
+ writeb(reg, TPU_REG(INT_CTRL));
+}
+
+void tpu_force_dsp_frame_irq(void)
+{
+ uint8_t reg = readb(TPU_REG(INT_CTRL));
+ reg |= ICTRL_DSP_FRAME_FORCE;
+ writeb(reg, TPU_REG(INT_CTRL));
+}
+
+uint16_t tpu_get_offset(void)
+{
+ return readw(TPU_REG(TPU_OFFSET));
+}
+
+uint16_t tpu_get_synchro(void)
+{
+ return readw(TPU_REG(TPU_SYNCHRO));
+}
+
+/* add two numbers, modulo 5000, and ensure the result is positive */
+uint16_t add_mod5000(int16_t a, int16_t b)
+{
+ int32_t sum = (int32_t)a + (int32_t)b;
+
+ sum %= 5000;
+
+ /* wrap around zero */
+ if (sum < 0)
+ sum += 5000;
+
+ return sum;
+}
diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c
new file mode 100644
index 00000000..5d24f48e
--- /dev/null
+++ b/src/target/firmware/calypso/tsp.c
@@ -0,0 +1,121 @@
+/* Calypso DBB internal TSP (Time Serial Port) Driver */
+
+/* (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 <memory.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+
+static uint16_t tspact_state;
+
+/* initiate a TSP write through the TPU */
+void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout)
+{
+ if (bitlen <= 8) {
+ tpu_enq_move(TPUI_TX_1, dout & 0xff);
+ } else if (bitlen <= 16) {
+ tpu_enq_move(TPUI_TX_1, (dout >> 8) & 0xff);
+ tpu_enq_move(TPUI_TX_2, dout & 0xff);
+ } else if (bitlen <= 24) {
+ tpu_enq_move(TPUI_TX_1, (dout >> 16) & 0xff);
+ tpu_enq_move(TPUI_TX_2, (dout >> 8) & 0xff);
+ tpu_enq_move(TPUI_TX_3, dout & 0xff);
+ } else {
+ tpu_enq_move(TPUI_TX_1, (dout >> 24) & 0xff);
+ tpu_enq_move(TPUI_TX_2, (dout >> 16) & 0xff);
+ tpu_enq_move(TPUI_TX_3, (dout >> 8) & 0xff);
+ tpu_enq_move(TPUI_TX_4, dout & 0xff);
+ }
+ tpu_enq_move(TPUI_TSP_CTRL1, (dev_idx << 5) | (bitlen - 1));
+ tpu_enq_move(TPUI_TSP_CTRL2, TPUI_CTRL2_WR);
+}
+
+/* Configure clock edge and chip enable polarity for a device */
+void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge)
+{
+ uint8_t reg = TPUI_TSP_SET1 + (dev_idx / 2);
+ uint8_t val = 0;
+ uint8_t shift;
+
+ if (dev_idx & 1)
+ shift = 4;
+ else
+ shift = 0;
+
+ if (clk_rising)
+ val |= 1;
+ if (en_positive)
+ val |= 2;
+ if (en_edge)
+ val |= 4;
+
+ tpu_enq_move(reg, (val << shift));
+}
+
+/* Update the TSPACT state, including enable and disable */
+void tsp_act_update(uint16_t new_act)
+{
+ uint8_t low = new_act & 0xff;
+ uint8_t high = new_act >> 8;
+
+ if (low != (tspact_state & 0xff))
+ tpu_enq_move(TPUI_TSP_ACT_L, low);
+ if (high != (tspact_state >> 8))
+ tpu_enq_move(TPUI_TSP_ACT_U, high);
+
+ tspact_state = new_act;
+}
+
+/* Enable one or multiple TSPACT signals */
+void tsp_act_enable(uint16_t bitmask)
+{
+ uint16_t new_act = tspact_state | bitmask;
+ tsp_act_update(new_act);
+}
+
+/* Disable one or multiple TSPACT signals */
+void tsp_act_disable(uint16_t bitmask)
+{
+ uint16_t new_act = tspact_state & ~bitmask;
+ tsp_act_update(new_act);
+}
+
+/* Obtain the current tspact state */
+uint16_t tsp_act_state(void)
+{
+ return tspact_state;
+}
+
+/* Toggle one or multiple TSPACT signals */
+void tsp_act_toggle(uint16_t bitmask)
+{
+ uint16_t new_act = tspact_state ^ bitmask;
+ tsp_act_update(new_act);
+}
+
+void tsp_init(void)
+{
+ tsp_act_update(0);
+}
diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c
new file mode 100644
index 00000000..a46fff91
--- /dev/null
+++ b/src/target/firmware/calypso/uart.c
@@ -0,0 +1,440 @@
+/* Calypso DBB internal UART Driver */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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 <debug.h>
+
+#include <memory.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <defines.h>
+#include <console.h>
+#include <comm/sercomm.h>
+
+#include <calypso/irq.h>
+#include <calypso/uart.h>
+
+#define BASE_ADDR_UART_MODEM 0xffff5000
+#define OFFSET_IRDA 0x800
+
+#define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m))
+
+#define LCR7BIT 0x80
+#define LCRBFBIT 0x40
+#define MCR6BIT 0x20
+#define REG_OFFS(m) ((m) &= ~(LCR7BIT|LCRBFBIT|MCR6BIT))
+/* read access LCR[7] = 0 */
+enum uart_reg {
+ RHR = 0,
+ IER = 1,
+ IIR = 2,
+ LCR = 3,
+ MCR = 4,
+ LSR = 5,
+ MSR = 6,
+ SPR = 7,
+ MDR1 = 8,
+ DMR2 = 9,
+ SFLSR = 0x0a,
+ RESUME = 0x0b,
+ SFREGL = 0x0c,
+ SFREGH = 0x0d,
+ BLR = 0x0e,
+ ACREG = 0x0f,
+ SCR = 0x10,
+ SSR = 0x11,
+ EBLR = 0x12,
+/* read access LCR[7] = 1 */
+ DLL = RHR | LCR7BIT,
+ DLH = IER | LCR7BIT,
+ DIV1_6 = ACREG | LCR7BIT,
+/* read/write access LCR[7:0] = 0xbf */
+ EFR = IIR | LCRBFBIT,
+ XON1 = MCR | LCRBFBIT,
+ XON2 = LSR | LCRBFBIT,
+ XOFF1 = MSR | LCRBFBIT,
+ XOFF2 = SPR | LCRBFBIT,
+/* read/write access if EFR[4] = 1 and MCR[6] = 1 */
+ TCR = MSR | MCR6BIT,
+ TLR = SPR | MCR6BIT,
+};
+/* write access LCR[7] = 0 */
+#define THR RHR
+#define FCR IIR /* only if EFR[4] = 1 */
+#define TXFLL SFLSR
+#define TXFLH RESUME
+#define RXFLL SFREGL
+#define RXFLH SFREGH
+
+enum fcr_bits {
+ FIFO_EN = (1 << 0),
+ RX_FIFO_CLEAR = (1 << 1),
+ TX_FIFO_CLEAR = (1 << 2),
+ DMA_MODE = (1 << 3),
+};
+#define TX_FIFO_TRIG_SHIFT 4
+#define RX_FIFO_TRIG_SHIFT 6
+
+enum iir_bits {
+ IIR_INT_PENDING = 0x01,
+ IIR_INT_TYPE = 0x3E,
+ IIR_INT_TYPE_RX_STATUS_ERROR = 0x06,
+ IIR_INT_TYPE_RX_TIMEOUT = 0x0C,
+ IIR_INT_TYPE_RHR = 0x04,
+ IIR_INT_TYPE_THR = 0x02,
+ IIR_INT_TYPE_MSR = 0x00,
+ IIR_INT_TYPE_XOFF = 0x10,
+ IIR_INT_TYPE_FLOW = 0x20,
+ IIR_FCR0_MIRROR = 0xC0,
+};
+
+#define UART_REG_UIR 0xffff6000
+
+/* enable or disable the divisor latch for access to DLL, DLH */
+static void uart_set_lcr7bit(int uart, int on)
+{
+ uint8_t reg;
+
+ reg = readb(UART_REG(uart, LCR));
+ if (on)
+ reg |= (1 << 7);
+ else
+ reg &= ~(1 << 7);
+ writeb(reg, UART_REG(uart, LCR));
+}
+
+static uint8_t old_lcr;
+static void uart_set_lcr_bf(int uart, int on)
+{
+ old_lcr = readb(UART_REG(uart, LCR));
+
+ if (on)
+ writeb(0xBF, UART_REG(uart, LCR));
+ else
+ writeb(old_lcr, UART_REG(uart, LCR));
+}
+
+/* Enable or disable the TCR_TLR latch bit in MCR[6] */
+static void uart_set_mcr6bit(int uart, int on)
+{
+ uint8_t mcr;
+ /* we assume EFR[4] is always set to 1 */
+ mcr = readb(UART_REG(uart, MCR));
+ if (on)
+ mcr |= (1 << 6);
+ else
+ mcr &= ~(1 << 6);
+ writeb(mcr, UART_REG(uart, MCR));
+}
+
+static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val)
+{
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 1);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 1);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 1);
+
+ writeb(val, UART_REG(uart, REG_OFFS(reg)));
+
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 0);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 0);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 0);
+}
+
+/* read from a UART register, applying any required latch bits */
+static uint8_t uart_reg_read(int uart, enum uart_reg reg)
+{
+ uint8_t ret;
+
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 1);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 1);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 1);
+
+ ret = readb(UART_REG(uart, REG_OFFS(reg)));
+
+ if (reg & LCRBFBIT)
+ uart_set_lcr_bf(uart, 0);
+ else if (reg & LCR7BIT)
+ uart_set_lcr7bit(uart, 0);
+ else if (reg & MCR6BIT)
+ uart_set_mcr6bit(uart, 0);
+
+ return ret;
+}
+
+static void uart_irq_handler_cons(__unused enum irq_nr irqnr)
+{
+ const uint8_t uart = CONS_UART_NR;
+ uint8_t iir;
+
+ //uart_putchar_nb(uart, 'U');
+
+ iir = uart_reg_read(uart, IIR);
+ if (iir & IIR_INT_PENDING)
+ return;
+
+ switch (iir & IIR_INT_TYPE) {
+ case IIR_INT_TYPE_RHR:
+ break;
+ case IIR_INT_TYPE_THR:
+ if (cons_rb_flush() == 1) {
+ /* everything was flushed, disable THR IRQ */
+ uint8_t ier = uart_reg_read(uart, IER);
+ ier &= ~(1 << 1);
+ uart_reg_write(uart, IER, ier);
+ }
+ break;
+ case IIR_INT_TYPE_MSR:
+ break;
+ case IIR_INT_TYPE_RX_STATUS_ERROR:
+ break;
+ case IIR_INT_TYPE_RX_TIMEOUT:
+ break;
+ case IIR_INT_TYPE_XOFF:
+ break;
+ }
+}
+
+static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr)
+{
+ const uint8_t uart = SERCOMM_UART_NR;
+ uint8_t iir, ch;
+
+ //uart_putchar_nb(uart, 'U');
+
+ iir = uart_reg_read(uart, IIR);
+ if (iir & IIR_INT_PENDING)
+ return;
+
+ switch (iir & IIR_INT_TYPE) {
+ case IIR_INT_TYPE_RX_TIMEOUT:
+ case IIR_INT_TYPE_RHR:
+ /* as long as we have rx data available */
+ while (uart_getchar_nb(uart, &ch)) {
+ if (sercomm_drv_rx_char(ch) < 0) {
+ /* sercomm cannot receive more data right now */
+ uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0);
+ }
+ }
+ break;
+ case IIR_INT_TYPE_THR:
+ /* as long as we have space in the FIFO */
+ while (!uart_tx_busy(uart)) {
+ /* get a byte from sercomm */
+ if (!sercomm_drv_pull(&ch)) {
+ /* no more bytes in sercomm, stop TX interrupts */
+ uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0);
+ break;
+ }
+ /* write the byte into the TX FIFO */
+ uart_putchar_nb(uart, ch);
+ }
+ break;
+ case IIR_INT_TYPE_MSR:
+ printf("UART IRQ MSR\n");
+ break;
+ case IIR_INT_TYPE_RX_STATUS_ERROR:
+ printf("UART IRQ RX_SE\n");
+ break;
+ case IIR_INT_TYPE_XOFF:
+ printf("UART IRQXOFF\n");
+ break;
+ }
+}
+
+static const uint8_t uart2irq[] = {
+ [0] = IRQ_UART_IRDA,
+ [1] = IRQ_UART_MODEM,
+};
+
+void uart_init(uint8_t uart, uint8_t interrupts)
+{
+ uint8_t irq = uart2irq[uart];
+
+ uart_reg_write(uart, IER, 0x00);
+ if (uart == CONS_UART_NR) {
+ cons_init();
+ if(interrupts) {
+ irq_register_handler(irq, &uart_irq_handler_cons);
+ irq_config(irq, 0, 0, 0xff);
+ irq_enable(irq);
+ }
+ } else {
+ sercomm_init();
+ if(interrupts) {
+ irq_register_handler(irq, &uart_irq_handler_sercomm);
+ irq_config(irq, 0, 0, 0xff);
+ irq_enable(irq);
+ }
+ uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1);
+ }
+#if 0
+ if (uart == 1) {
+ /* assign UART to MCU and unmask interrupts*/
+ writeb(UART_REG_UIR, 0x00);
+ }
+#endif
+
+ /* if we don't initialize these, we get strange corruptions in the
+ received data... :-( */
+ uart_reg_write(uart, MDR1, 0x07); /* turn off UART */
+ uart_reg_write(uart, XON1, 0x00); /* Xon1/Addr Register */
+ uart_reg_write(uart, XON2, 0x00); /* Xon2/Addr Register */
+ uart_reg_write(uart, XOFF1, 0x00); /* Xoff1 Register */
+ uart_reg_write(uart, XOFF2, 0x00); /* Xoff2 Register */
+ uart_reg_write(uart, EFR, 0x00); /* Enhanced Features Register */
+
+ /* select UART mode */
+ uart_reg_write(uart, MDR1, 0);
+ /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */
+ uart_reg_write(uart, EFR, (1 << 4));
+ /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */
+ uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR |
+ (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT));
+
+ /* THR interrupt only when TX FIFO and TX shift register are empty */
+ uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3));
+
+ /* 8 bit, 1 stop bit, no parity, no break */
+ uart_reg_write(uart, LCR, 0x03);
+
+ uart_set_lcr7bit(uart, 0);
+}
+
+void uart_poll(uint8_t uart) {
+ if(uart == CONS_UART_NR) {
+ uart_irq_handler_cons(0);
+ } else {
+ uart_irq_handler_sercomm(0);
+ }
+}
+
+void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on)
+{
+ uint8_t ier = uart_reg_read(uart, IER);
+ uint8_t mask = 0;
+
+ switch (irq) {
+ case UART_IRQ_TX_EMPTY:
+ mask = (1 << 1);
+ break;
+ case UART_IRQ_RX_CHAR:
+ mask = (1 << 0);
+ break;
+ }
+
+ if (on)
+ ier |= mask;
+ else
+ ier &= ~mask;
+
+ uart_reg_write(uart, IER, ier);
+}
+
+
+void uart_putchar_wait(uint8_t uart, int c)
+{
+ /* wait while TX FIFO indicates full */
+ while (readb(UART_REG(uart, SSR)) & 0x01) { }
+
+ /* put character in TX FIFO */
+ writeb(c, UART_REG(uart, THR));
+}
+
+int uart_putchar_nb(uint8_t uart, int c)
+{
+ /* if TX FIFO indicates full, abort */
+ if (readb(UART_REG(uart, SSR)) & 0x01)
+ return 0;
+
+ writeb(c, UART_REG(uart, THR));
+ return 1;
+}
+
+int uart_getchar_nb(uint8_t uart, uint8_t *ch)
+{
+ uint8_t lsr;
+
+ lsr = readb(UART_REG(uart, LSR));
+
+ /* something strange happened */
+ if (lsr & 0x02)
+ printf("LSR RX_OE\n");
+ if (lsr & 0x04)
+ printf("LSR RX_PE\n");
+ if (lsr & 0x08)
+ printf("LSR RX_FE\n");
+ if (lsr & 0x10)
+ printf("LSR RX_BI\n");
+ if (lsr & 0x80)
+ printf("LSR RX_FIFO_STS\n");
+
+ /* is the Rx FIFO empty? */
+ if (!(lsr & 0x01))
+ return 0;
+
+ *ch = readb(UART_REG(uart, RHR));
+ //printf("getchar_nb(%u) = %02x\n", uart, *ch);
+ return 1;
+}
+
+int uart_tx_busy(uint8_t uart)
+{
+ if (readb(UART_REG(uart, SSR)) & 0x01)
+ return 1;
+ return 0;
+}
+
+static const uint16_t divider[] = {
+ [UART_38400] = 21, /* 38,690 */
+ [UART_57600] = 14, /* 58,035 */
+ [UART_115200] = 7, /* 116,071 */
+ [UART_230400] = 4, /* 203,125! (-3% would be 223,488) */
+ [UART_460800] = 2, /* 406,250! (-3% would be 446,976) */
+ [UART_921600] = 1, /* 812,500! (-3% would be 893,952) */
+};
+
+int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt)
+{
+ uint16_t div;
+
+ if (bdrt > ARRAY_SIZE(divider))
+ return -1;
+
+ div = divider[bdrt];
+ uart_set_lcr7bit(uart, 1);
+ writeb(div & 0xff, UART_REG(uart, DLL));
+ writeb(div >> 8, UART_REG(uart, DLH));
+ uart_set_lcr7bit(uart, 0);
+
+ return 0;
+}
diff --git a/src/target/firmware/calypso/uwire.c b/src/target/firmware/calypso/uwire.c
new file mode 100644
index 00000000..b79d9f38
--- /dev/null
+++ b/src/target/firmware/calypso/uwire.c
@@ -0,0 +1,136 @@
+/* Driver for uWire Master Controller inside TI Calypso */
+
+/* (C) 2010 by Sylvain Munaut <tnt@246tNt.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 <stdint.h>
+#include <stdio.h>
+
+//#define DEBUG
+#include <debug.h>
+
+#include <memory.h>
+#include <uwire.h>
+#include <delay.h>
+
+#define BASE_ADDR_UWIRE 0xfffe4000
+#define UWIRE_REG(n) (BASE_ADDR_UWIRE+(n))
+
+enum uwire_regs {
+ REG_DATA = 0x00,
+ REG_CSR = 0x02,
+ REG_SR1 = 0x04,
+ REG_SR2 = 0x06,
+ REG_SR3 = 0x08,
+};
+
+#define UWIRE_CSR_BITS_RD(n) (((n) & 0x1f) << 0)
+#define UWIRE_CSR_BITS_WR(n) (((n) & 0x1f) << 5)
+#define UWIRE_CSR_IDX(n) (((n) & 3) << 10)
+#define UWIRE_CSR_CS_CMD (1 << 12)
+#define UWIRE_CSR_START (1 << 13)
+#define UWIRE_CSR_CSRB (1 << 14)
+#define UWIRE_CSR_RDRB (1 << 15)
+
+#define UWIRE_CSn_EDGE_RD (1 << 0) /* 1=falling 0=rising */
+#define UWIRE_CSn_EDGE_WR (1 << 1) /* 1=falling 0=rising */
+#define UWIRE_CSn_CS_LVL (1 << 2)
+#define UWIRE_CSn_FRQ_DIV2 (0 << 3)
+#define UWIRE_CSn_FRQ_DIV4 (1 << 3)
+#define UWIRE_CSn_FRQ_DIV8 (2 << 3)
+#define UWIRE_CSn_CKH
+
+#define UWIRE_CSn_SHIFT(n) (((n) & 1) ? 6 : 0)
+#define UWIRE_CSn_REG(n) (((n) & 2) ? REG_SR2 : REG_SR1)
+
+#define UWIRE_SR3_CLK_EN (1 << 0)
+#define UWIRE_SR3_CLK_DIV2 (0 << 1)
+#define UWIRE_SR3_CLK_DIV4 (1 << 1)
+#define UWIRE_SR3_CLK_DIV7 (2 << 1)
+#define UWIRE_SR3_CLK_DIV10 (3 << 1)
+
+static inline void _uwire_wait(int mask, int val)
+{
+ while ((readw(UWIRE_REG(REG_CSR)) & mask) != val);
+}
+
+void uwire_init(void)
+{
+ writew(UWIRE_SR3_CLK_EN | UWIRE_SR3_CLK_DIV2, UWIRE_REG(REG_SR3));
+ /* FIXME only init CS0 for now */
+ writew(((UWIRE_CSn_CS_LVL | UWIRE_CSn_FRQ_DIV2) << UWIRE_CSn_SHIFT(0)),
+ UWIRE_REG(UWIRE_CSn_REG(0)));
+ writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR));
+ _uwire_wait(UWIRE_CSR_CSRB, 0);
+}
+
+int uwire_xfer(int cs, int bitlen, const void *dout, void *din)
+{
+ uint16_t tmp = 0;
+
+ if (bitlen <= 0 || bitlen > 16)
+ return -1;
+ if (cs < 0 || cs > 4)
+ return -1;
+
+ /* FIXME uwire_init always select CS0 for now */
+
+ printd("uwire_xfer(dev_idx=%u, bitlen=%u\n", cs, bitlen);
+
+ /* select the chip */
+ writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR));
+ _uwire_wait(UWIRE_CSR_CSRB, 0);
+
+ if (dout) {
+ if (bitlen <= 8)
+ tmp = *(uint8_t *)dout;
+ else if (bitlen <= 16)
+ tmp = *(uint16_t *)dout;
+ tmp <<= 16 - bitlen; /* align to MSB */
+ writew(tmp, UWIRE_REG(REG_DATA));
+ printd(", data_out=0x%04hx", tmp);
+ }
+
+ tmp = (dout ? UWIRE_CSR_BITS_WR(bitlen) : 0) |
+ (din ? UWIRE_CSR_BITS_RD(bitlen) : 0) |
+ UWIRE_CSR_START;
+ writew(tmp, UWIRE_REG(REG_CSR));
+
+ _uwire_wait(UWIRE_CSR_CSRB, 0);
+
+ if (din) {
+ _uwire_wait(UWIRE_CSR_RDRB, UWIRE_CSR_RDRB);
+
+ tmp = readw(UWIRE_REG(REG_DATA));
+ printd(", data_in=0x%08x", tmp);
+
+ if (bitlen <= 8)
+ *(uint8_t *)din = tmp & 0xff;
+ else if (bitlen <= 16)
+ *(uint16_t *)din = tmp & 0xffff;
+ }
+ /* unselect the chip */
+ writew(UWIRE_CSR_IDX(0) | 0, UWIRE_REG(REG_CSR));
+ _uwire_wait(UWIRE_CSR_CSRB, 0);
+
+ printd(")\n");
+
+ return 0;
+}
diff --git a/src/target/firmware/comm/Makefile b/src/target/firmware/comm/Makefile
new file mode 100644
index 00000000..25fbb983
--- /dev/null
+++ b/src/target/firmware/comm/Makefile
@@ -0,0 +1,5 @@
+
+LIBRARIES+=comm
+comm_DIR=comm
+comm_SRCS=msgb.c sercomm.c sercomm_cons.c timer.c
+
diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c
new file mode 100644
index 00000000..d412844c
--- /dev/null
+++ b/src/target/firmware/comm/msgb.c
@@ -0,0 +1,74 @@
+/* (C) 2008-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 <string.h>
+#include <sys/types.h>
+
+#include <debug.h>
+#include <delay.h>
+
+#include <osmocore/msgb.h>
+
+#include <calypso/backlight.h>
+
+#define NO_TALLOC
+
+void *tall_msgb_ctx;
+
+#ifdef NO_TALLOC
+/* This is a poor mans static allocator for msgb objects */
+#define MSGB_DATA_SIZE 256+4
+#define MSGB_NUM 32
+struct supermsg {
+ uint8_t allocated;
+ struct msgb msg;
+ uint8_t buf[MSGB_DATA_SIZE];
+};
+static struct supermsg msgs[MSGB_NUM];
+void *_talloc_zero(void *ctx, unsigned int size, const char *name)
+{
+ unsigned int i;
+ if (size > sizeof(struct msgb) + MSGB_DATA_SIZE)
+ goto panic;
+
+ while (1) {
+ for (i = 0; i < ARRAY_SIZE(msgs); i++) {
+ if (!msgs[i].allocated) {
+ msgs[i].allocated = 1;
+ memset(&msgs[i].msg, 0, sizeof(&msgs[i].msg));
+ memset(&msgs[i].buf, 0, sizeof(&msgs[i].buf));
+ return &msgs[i].msg;
+ }
+ }
+ cons_puts("unable to allocate msgb\n");
+ bl_level(++i % 50);
+ delay_ms(50);
+ }
+panic:
+ return NULL;
+}
+void talloc_free(void *msg)
+{
+ struct supermsg *smsg = container_of(msg, struct supermsg, msg);
+ smsg->allocated = 0;
+}
+#endif
diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c
new file mode 100644
index 00000000..cace0465
--- /dev/null
+++ b/src/target/firmware/comm/sercomm.c
@@ -0,0 +1,277 @@
+/* Serial communications layer, based on HDLC */
+
+/* (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 <errno.h>
+
+#include <osmocore/msgb.h>
+
+#ifdef HOST_BUILD
+#define SERCOMM_RX_MSG_SIZE 2048
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#endif
+#include <sercomm.h>
+#define local_irq_save(x)
+#define local_fiq_disable()
+#define local_irq_restore(x)
+
+#else
+#define SERCOMM_RX_MSG_SIZE 256
+#include <debug.h>
+#include <osmocore/linuxlist.h>
+#include <asm/system.h>
+
+#include <comm/sercomm.h>
+#include <calypso/uart.h>
+#endif
+
+
+enum rx_state {
+ RX_ST_WAIT_START,
+ RX_ST_ADDR,
+ RX_ST_CTRL,
+ RX_ST_DATA,
+ RX_ST_ESCAPE,
+};
+
+static struct {
+ int initialized;
+
+ /* transmit side */
+ struct {
+ struct llist_head dlci_queues[_SC_DLCI_MAX];
+ struct msgb *msg;
+ enum rx_state state;
+ uint8_t *next_char;
+ } tx;
+
+ /* receive side */
+ struct {
+ dlci_cb_t dlci_handler[_SC_DLCI_MAX];
+ struct msgb *msg;
+ enum rx_state state;
+ uint8_t dlci;
+ uint8_t ctrl;
+ } rx;
+
+} sercomm;
+
+void sercomm_init(void)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
+ INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]);
+
+ sercomm.rx.msg = NULL;
+ sercomm.initialized = 1;
+
+ /* set up the echo dlci */
+ sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg);
+}
+
+int sercomm_initialized(void)
+{
+ return sercomm.initialized;
+}
+
+/* user interface for transmitting messages for a given DLCI */
+void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
+{
+ unsigned long flags;
+ uint8_t *hdr;
+
+ /* prepend address + control octet */
+ hdr = msgb_push(msg, 2);
+ hdr[0] = dlci;
+ hdr[1] = HDLC_C_UI;
+
+ /* This functiion can be called from any context: FIQ, IRQ
+ * and supervisor context. Proper locking is important! */
+ local_irq_save(flags);
+ local_fiq_disable();
+ msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
+ local_irq_restore(flags);
+
+#ifndef HOST_BUILD
+ /* tell UART that we have something to send */
+ uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1);
+#endif
+}
+
+/* how deep is the Tx queue for a given DLCI */
+unsigned int sercomm_tx_queue_depth(uint8_t dlci)
+{
+ struct llist_head *le;
+ unsigned int num = 0;
+
+ llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
+ num++;
+ }
+
+ return num;
+}
+
+/* fetch one octet of to-be-transmitted serial data */
+int sercomm_drv_pull(uint8_t *ch)
+{
+ /* we are always called from interrupt context in this function,
+ * which means that any data structures we use need to be for
+ * our exclusive access */
+ if (!sercomm.tx.msg) {
+ unsigned int i;
+ /* dequeue a new message from the queues */
+ for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) {
+ sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]);
+ if (sercomm.tx.msg)
+ break;
+ }
+ if (sercomm.tx.msg) {
+ /* start of a new message, send start flag octet */
+ *ch = HDLC_FLAG;
+ sercomm.tx.next_char = sercomm.tx.msg->data;
+ return 1;
+ } else {
+ /* no more data avilable */
+ return 0;
+ }
+ }
+
+ if (sercomm.tx.state == RX_ST_ESCAPE) {
+ /* we've already transmitted the ESCAPE octet,
+ * we now need to trnsmit the escaped data */
+ *ch = *sercomm.tx.next_char++;
+ sercomm.tx.state = RX_ST_DATA;
+ } else if (sercomm.tx.next_char >= sercomm.tx.msg->tail) {
+ /* last character has already been transmitted,
+ * send end-of-message octet */
+ *ch = HDLC_FLAG;
+ /* we've reached the end of the message buffer */
+ msgb_free(sercomm.tx.msg);
+ sercomm.tx.msg = NULL;
+ sercomm.tx.next_char = NULL;
+ /* escaping for the two control octets */
+ } else if (*sercomm.tx.next_char == HDLC_FLAG ||
+ *sercomm.tx.next_char == HDLC_ESCAPE ||
+ *sercomm.tx.next_char == 0x00) {
+ /* send an escape octet */
+ *ch = HDLC_ESCAPE;
+ /* invert bit 5 of the next octet to be sent */
+ *sercomm.tx.next_char ^= (1 << 5);
+ sercomm.tx.state = RX_ST_ESCAPE;
+ } else {
+ /* standard case, simply send next octet */
+ *ch = *sercomm.tx.next_char++;
+ }
+ return 1;
+}
+
+/* register a handler for a given DLCI */
+int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
+{
+ if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
+ return -EINVAL;
+
+ if (sercomm.rx.dlci_handler[dlci])
+ return -EBUSY;
+
+ sercomm.rx.dlci_handler[dlci] = cb;
+ return 0;
+}
+
+/* dispatch an incomnig message once it is completely received */
+static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
+{
+ if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) ||
+ !sercomm.rx.dlci_handler[dlci]) {
+ msgb_free(msg);
+ return;
+ }
+ sercomm.rx.dlci_handler[dlci](dlci, msg);
+}
+
+/* the driver has received one byte, pass it into sercomm layer */
+int sercomm_drv_rx_char(uint8_t ch)
+{
+ uint8_t *ptr;
+
+ /* we are always called from interrupt context in this function,
+ * which means that any data structures we use need to be for
+ * our exclusive access */
+ if (!sercomm.rx.msg)
+ sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
+
+ if (msgb_tailroom(sercomm.rx.msg) == 0) {
+ //cons_puts("sercomm_drv_rx_char() overflow!\n");
+ msgb_free(sercomm.rx.msg);
+ sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
+ sercomm.rx.state = RX_ST_WAIT_START;
+ return 0;
+ }
+
+ switch (sercomm.rx.state) {
+ case RX_ST_WAIT_START:
+ if (ch != HDLC_FLAG)
+ break;
+ sercomm.rx.state = RX_ST_ADDR;
+ break;
+ case RX_ST_ADDR:
+ sercomm.rx.dlci = ch;
+ sercomm.rx.state = RX_ST_CTRL;
+ break;
+ case RX_ST_CTRL:
+ sercomm.rx.ctrl = ch;
+ sercomm.rx.state = RX_ST_DATA;
+ break;
+ case RX_ST_DATA:
+ if (ch == HDLC_ESCAPE) {
+ /* drop the escape octet, but change state */
+ sercomm.rx.state = RX_ST_ESCAPE;
+ break;
+ } else if (ch == HDLC_FLAG) {
+ /* message is finished */
+ dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
+ /* allocate new buffer */
+ sercomm.rx.msg = NULL;
+ /* start all over again */
+ sercomm.rx.state = RX_ST_WAIT_START;
+
+ /* do not add the control char */
+ break;
+ }
+ /* default case: store the octet */
+ ptr = msgb_put(sercomm.rx.msg, 1);
+ *ptr = ch;
+ break;
+ case RX_ST_ESCAPE:
+ /* store bif-5-inverted octet in buffer */
+ ch ^= (1 << 5);
+ ptr = msgb_put(sercomm.rx.msg, 1);
+ *ptr = ch;
+ /* transition back to nromal DATA state */
+ sercomm.rx.state = RX_ST_DATA;
+ break;
+ }
+
+ return 1;
+}
diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c
new file mode 100644
index 00000000..5d7842fb
--- /dev/null
+++ b/src/target/firmware/comm/sercomm_cons.c
@@ -0,0 +1,140 @@
+/* Serial console layer, layered on top of sercomm HDLC */
+
+/* (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 <errno.h>
+#include <string.h>
+
+#include <asm/system.h>
+
+#include <calypso/uart.h>
+
+#include <console.h>
+#include <osmocore/msgb.h>
+#include <comm/sercomm.h>
+#include <comm/sercomm_cons.h>
+
+static struct {
+ struct msgb *cur_msg;
+} scons;
+
+static void raw_puts(const char *s)
+{
+ int i = strlen(s);
+ while (i--)
+ uart_putchar_wait(SERCOMM_UART_NR, *s++);
+}
+
+#ifdef DEBUG
+#define raw_putd(x) raw_puts(x)
+#else
+#define raw_putd(x)
+#endif
+
+int sercomm_puts(const char *s)
+{
+ unsigned long flags;
+ const int len = strlen(s);
+ unsigned int bytes_left = len;
+
+ if (!sercomm_initialized()) {
+ raw_putd("sercomm not initialized: ");
+ raw_puts(s);
+ return len - 1;
+ }
+
+ /* This function is called from any context: Supervisor, IRQ, FIQ, ...
+ * as such, we need to ensure re-entrant calls are either supported or
+ * avoided. */
+ local_irq_save(flags);
+ local_fiq_disable();
+
+ while (bytes_left > 0) {
+ unsigned int write_num, space_left, flush;
+ uint8_t *data;
+
+ if (!scons.cur_msg)
+ scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC);
+
+ if (!scons.cur_msg) {
+ raw_putd("cannot allocate sercomm msgb: ");
+ raw_puts(s);
+ return -ENOMEM;
+ }
+
+ /* space left in the current msgb */
+ space_left = msgb_tailroom(scons.cur_msg);
+
+ if (space_left <= bytes_left) {
+ write_num = space_left;
+ /* flush buffer when it is full */
+ flush = 1;
+ } else {
+ write_num = bytes_left;
+ flush = 0;
+ }
+
+ /* obtain pointer where to copy the data */
+ data = msgb_put(scons.cur_msg, write_num);
+
+ /* copy data while looking for \n line termination */
+ {
+ unsigned int i;
+ for (i = 0; i < write_num; i++) {
+ /* flush buffer at end of line, but skip
+ * flushing if we have a backlog in order to
+ * increase efficiency of msgb filling */
+ if (*s == '\n' &&
+ sercomm_tx_queue_depth(SC_DLCI_CONSOLE) < 4)
+ flush = 1;
+ *data++ = *s++;
+ }
+ }
+ bytes_left -= write_num;
+
+ if (flush) {
+ sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg);
+ /* reset scons.cur_msg pointer to ensure we allocate
+ * a new one next round */
+ scons.cur_msg = NULL;
+ }
+ }
+
+ local_irq_restore(flags);
+
+ return len - 1;
+}
+
+int sercomm_putchar(int c)
+{
+ char s[2];
+ int rc;
+
+ s[0] = c & 0xff;
+ s[1] = '\0';
+
+ rc = sercomm_puts(s);
+ if (rc < 0)
+ return rc;
+
+ return c;
+}
diff --git a/src/target/firmware/comm/timer.c b/src/target/firmware/comm/timer.c
new file mode 100644
index 00000000..d69ef542
--- /dev/null
+++ b/src/target/firmware/comm/timer.c
@@ -0,0 +1,213 @@
+/* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (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 <debug.h>
+#include <osmocore/linuxlist.h>
+
+#include <comm/timer.h>
+
+#include <calypso/timer.h>
+#include <calypso/irq.h>
+
+static LLIST_HEAD(timer_list);
+
+unsigned long volatile jiffies;
+
+#define TIMER_HZ 100
+
+#define time_after(a,b) \
+ (typecheck(unsigned long, a) && \
+ typecheck(unsigned long, b) && \
+ ((long)(b) - (long)(a) < 0))
+#define time_before(a,b) time_after(b,a)
+
+void add_timer(struct timer_list *timer)
+{
+ struct timer_list *list_timer;
+
+ /* TODO: Optimize and remember the closest item... */
+ timer->active = 1;
+
+ /* this might be called from within update_timers */
+ llist_for_each_entry(list_timer, &timer_list, entry)
+ if (timer == list_timer)
+ return;
+
+ timer->in_list = 1;
+ llist_add(&timer->entry, &timer_list);
+}
+
+void schedule_timer(struct timer_list *timer, int milliseconds)
+{
+ timer->expires = jiffies + ((milliseconds * TIMER_HZ) / 1000);
+ add_timer(timer);
+}
+
+void del_timer(struct timer_list *timer)
+{
+ if (timer->in_list) {
+ timer->active = 0;
+ timer->in_list = 0;
+ llist_del(&timer->entry);
+ }
+}
+
+int timer_pending(struct timer_list *timer)
+{
+ return timer->active;
+}
+
+#if 0
+/*
+ * if we have a nearest time return the delta between the current
+ * time and the time of the nearest timer.
+ * If the nearest timer timed out return NULL and then we will
+ * dispatch everything after the select
+ */
+struct timeval *nearest_timer()
+{
+ struct timeval current_time;
+
+ if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
+ return NULL;
+
+ if (gettimeofday(&current_time, NULL) == -1)
+ return NULL;
+
+ unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec;
+ unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+
+ if (nearestTime < currentTime) {
+ s_select_time.tv_sec = 0;
+ s_select_time.tv_usec = 0;
+ } else {
+ s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
+ s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
+ }
+
+ return &s_select_time;
+}
+
+/*
+ * Find the nearest time and update s_nearest_time
+ */
+void prepare_timers()
+{
+ struct timer_list *timer, *nearest_timer = NULL;
+ llist_for_each_entry(timer, &timer_list, entry) {
+ if (!nearest_timer || time_before(timer->expires, nearest_timer->expires)) {
+ nearest_timer = timer;
+ }
+ }
+
+ if (nearest_timer) {
+ s_nearest_time = nearest_timer->timeout;
+ } else {
+ memset(&s_nearest_time, 0, sizeof(struct timeval));
+ }
+}
+#endif
+
+/*
+ * fire all timers... and remove them
+ */
+int update_timers(void)
+{
+ struct timer_list *timer, *tmp;
+ int work = 0;
+
+ /*
+ * The callbacks might mess with our list and in this case
+ * even llist_for_each_entry_safe is not safe to use. To allow
+ * del_timer, add_timer, schedule_timer to be called from within
+ * the callback we jump through some loops.
+ *
+ * First we set the handled flag of each active timer to zero,
+ * then we iterate over the list and execute the callbacks. As the
+ * list might have been changed (specially the next) from within
+ * the callback we have to start over again. Once every callback
+ * is dispatched we will remove the non-active from the list.
+ *
+ * TODO: If this is a performance issue we can poison a global
+ * variable in add_timer and del_timer and only then restart.
+ */
+ llist_for_each_entry(timer, &timer_list, entry) {
+ timer->handled = 0;
+ }
+
+restart:
+ llist_for_each_entry(timer, &timer_list, entry) {
+ if (!timer->handled && time_before(timer->expires, jiffies)) {
+ timer->handled = 1;
+ timer->active = 0;
+ (*timer->cb)(timer->data);
+ work = 1;
+ goto restart;
+ }
+ }
+
+ llist_for_each_entry_safe(timer, tmp, &timer_list, entry) {
+ timer->handled = 0;
+ if (!timer->active) {
+ del_timer(timer);
+ }
+ }
+
+ return work;
+}
+
+int timer_check(void)
+{
+ struct timer_list *timer;
+ int i = 0;
+
+ llist_for_each_entry(timer, &timer_list, entry) {
+ i++;
+ }
+ return i;
+}
+
+static void timer_irq(enum irq_nr irq)
+{
+ /* we only increment jiffies here. FIXME: does this need to be atomic? */
+ jiffies++;
+}
+
+void timer_init(void)
+{
+ /* configure TIMER2 for our purpose */
+ hwtimer_enable(2, 1);
+ /* The timer runs at 13MHz / 32, i.e. 406.25kHz */
+#if (TIMER_HZ == 100)
+ hwtimer_load(2, 4062);
+ hwtimer_config(2, 0, 1);
+#elif (TIMER_HZ == 10)
+ /* prescaler 4, 1015 ticks until expiry */
+ hwtimer_load(2, 1015);
+ hwtimer_config(2, 4, 1);
+#endif
+ hwtimer_enable(2, 1);
+
+ /* register interrupt handler with default priority, EDGE triggered */
+ irq_register_handler(IRQ_TIMER2, &timer_irq);
+ irq_config(IRQ_TIMER2, 0, 1, -1);
+ irq_enable(IRQ_TIMER2);
+}
diff --git a/src/target/firmware/display/display.c b/src/target/firmware/display/display.c
new file mode 100644
index 00000000..1c8f1fb4
--- /dev/null
+++ b/src/target/firmware/display/display.c
@@ -0,0 +1,20 @@
+
+#include <stdint.h>
+
+#include <display.h>
+
+struct display_driver *display;
+
+int display_puts(const char *str)
+{
+ char c;
+
+ if (display->puts)
+ display->puts(str);
+ else {
+ while ((c = *str++))
+ display_putchar(c);
+ }
+
+ return 0;
+}
diff --git a/src/target/firmware/display/font_r8x8.c b/src/target/firmware/display/font_r8x8.c
new file mode 100644
index 00000000..f6a8a820
--- /dev/null
+++ b/src/target/firmware/display/font_r8x8.c
Binary files differ
diff --git a/src/target/firmware/display/font_r8x8_horiz.c b/src/target/firmware/display/font_r8x8_horiz.c
new file mode 100644
index 00000000..046d09bf
--- /dev/null
+++ b/src/target/firmware/display/font_r8x8_horiz.c
@@ -0,0 +1,261 @@
+/* 8x8 font, right aligned, horizontal scanning */
+
+const unsigned char fontdata_r8x8_horiz[] ={
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e,
+ 0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e,
+ 0x6c,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,
+ 0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,
+ 0x1c,0x1c,0x1c,0x7f,0x7f,0x6b,0x08,0x1c,
+ 0x10,0x10,0x38,0x7c,0xfe,0x7c,0x10,0x38,
+ 0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,
+ 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,
+ 0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,
+ 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,
+ 0x0f,0x07,0x0f,0x7d,0xcc,0xcc,0xcc,0x78,
+ 0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,
+ 0x3f,0x33,0x3f,0x30,0x30,0x70,0xf0,0xe0,
+ 0x7f,0x63,0x7f,0x63,0x63,0x67,0xe6,0xc0,
+ 0x18,0xdb,0x3c,0xe7,0xe7,0x3c,0xdb,0x18,
+ 0x80,0xe0,0xf8,0xfe,0xf8,0xe0,0x80,0x00,
+ 0x02,0x0e,0x3e,0xfe,0x3e,0x0e,0x02,0x00,
+ 0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18,
+ 0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00,
+ 0x7f,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x00,
+ 0x3e,0x63,0x38,0x6c,0x6c,0x38,0xcc,0x78,
+ 0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00,
+ 0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff,
+ 0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00,
+ 0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,
+ 0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,
+ 0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,
+ 0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,
+ 0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,
+ 0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00,
+ 0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00,
+ 0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,
+ 0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00,
+ 0x18,0x3e,0x60,0x3c,0x06,0x7c,0x18,0x00,
+ 0x00,0x63,0x66,0x0c,0x18,0x33,0x63,0x00,
+ 0x1c,0x36,0x1c,0x3b,0x6e,0x66,0x3b,0x00,
+ 0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,
+ 0x0c,0x18,0x30,0x30,0x30,0x18,0x0c,0x00,
+ 0x30,0x18,0x0c,0x0c,0x0c,0x18,0x30,0x00,
+ 0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,
+ 0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30,
+ 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,
+ 0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00,
+ 0x3e,0x63,0x67,0x6f,0x7b,0x73,0x3e,0x00,
+ 0x18,0x38,0x58,0x18,0x18,0x18,0x7e,0x00,
+ 0x3c,0x66,0x06,0x1c,0x30,0x66,0x7e,0x00,
+ 0x3c,0x66,0x06,0x1c,0x06,0x66,0x3c,0x00,
+ 0x0e,0x1e,0x36,0x66,0x7f,0x06,0x0f,0x00,
+ 0x7e,0x60,0x7c,0x06,0x06,0x66,0x3c,0x00,
+ 0x1c,0x30,0x60,0x7c,0x66,0x66,0x3c,0x00,
+ 0x7e,0x66,0x06,0x0c,0x18,0x18,0x18,0x00,
+ 0x3c,0x66,0x66,0x3c,0x66,0x66,0x3c,0x00,
+ 0x3c,0x66,0x66,0x3e,0x06,0x0c,0x38,0x00,
+ 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00,
+ 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30,
+ 0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,
+ 0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,
+ 0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00,
+ 0x3c,0x66,0x06,0x0c,0x18,0x00,0x18,0x00,
+ 0x3e,0x63,0x6f,0x69,0x6f,0x60,0x3e,0x00,
+ 0x18,0x3c,0x66,0x66,0x7e,0x66,0x66,0x00,
+ 0x7e,0x33,0x33,0x3e,0x33,0x33,0x7e,0x00,
+ 0x1e,0x33,0x60,0x60,0x60,0x33,0x1e,0x00,
+ 0x7c,0x36,0x33,0x33,0x33,0x36,0x7c,0x00,
+ 0x7f,0x31,0x34,0x3c,0x34,0x31,0x7f,0x00,
+ 0x7f,0x31,0x34,0x3c,0x34,0x30,0x78,0x00,
+ 0x1e,0x33,0x60,0x60,0x67,0x33,0x1f,0x00,
+ 0x66,0x66,0x66,0x7e,0x66,0x66,0x66,0x00,
+ 0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,
+ 0x0f,0x06,0x06,0x06,0x66,0x66,0x3c,0x00,
+ 0x73,0x33,0x36,0x3c,0x36,0x33,0x73,0x00,
+ 0x78,0x30,0x30,0x30,0x31,0x33,0x7f,0x00,
+ 0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,
+ 0x63,0x73,0x7b,0x6f,0x67,0x63,0x63,0x00,
+ 0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,
+ 0x7e,0x33,0x33,0x3e,0x30,0x30,0x78,0x00,
+ 0x3c,0x66,0x66,0x66,0x6e,0x3c,0x0e,0x00,
+ 0x7e,0x33,0x33,0x3e,0x36,0x33,0x73,0x00,
+ 0x3c,0x66,0x30,0x18,0x0c,0x66,0x3c,0x00,
+ 0x7e,0x5a,0x18,0x18,0x18,0x18,0x3c,0x00,
+ 0x66,0x66,0x66,0x66,0x66,0x66,0x7e,0x00,
+ 0x66,0x66,0x66,0x66,0x66,0x3c,0x18,0x00,
+ 0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,
+ 0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,
+ 0x66,0x66,0x66,0x3c,0x18,0x18,0x3c,0x00,
+ 0x7f,0x63,0x46,0x0c,0x19,0x33,0x7f,0x00,
+ 0x3c,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,
+ 0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,
+ 0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,
+ 0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,
+ 0x18,0x18,0x0c,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x3c,0x06,0x3e,0x66,0x3b,0x00,
+ 0x70,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00,
+ 0x00,0x00,0x3c,0x66,0x60,0x66,0x3c,0x00,
+ 0x0e,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00,
+ 0x00,0x00,0x3c,0x66,0x7e,0x60,0x3c,0x00,
+ 0x1c,0x36,0x30,0x78,0x30,0x30,0x78,0x00,
+ 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x7c,
+ 0x70,0x30,0x36,0x3b,0x33,0x33,0x73,0x00,
+ 0x18,0x00,0x38,0x18,0x18,0x18,0x3c,0x00,
+ 0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3c,
+ 0x70,0x30,0x33,0x36,0x3c,0x36,0x73,0x00,
+ 0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,
+ 0x00,0x00,0x66,0x7f,0x7f,0x6b,0x63,0x00,
+ 0x00,0x00,0x7c,0x66,0x66,0x66,0x66,0x00,
+ 0x00,0x00,0x3c,0x66,0x66,0x66,0x3c,0x00,
+ 0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,
+ 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,
+ 0x00,0x00,0x6e,0x3b,0x33,0x30,0x78,0x00,
+ 0x00,0x00,0x3e,0x60,0x3c,0x06,0x7c,0x00,
+ 0x08,0x18,0x3e,0x18,0x18,0x1a,0x0c,0x00,
+ 0x00,0x00,0x66,0x66,0x66,0x66,0x3b,0x00,
+ 0x00,0x00,0x66,0x66,0x66,0x3c,0x18,0x00,
+ 0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,
+ 0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,
+ 0x00,0x00,0x66,0x66,0x66,0x3e,0x06,0x7c,
+ 0x00,0x00,0x7e,0x4c,0x18,0x32,0x7e,0x00,
+ 0x0e,0x18,0x18,0x70,0x18,0x18,0x0e,0x00,
+ 0x0c,0x0c,0x0c,0x00,0x0c,0x0c,0x0c,0x00,
+ 0x70,0x18,0x18,0x0e,0x18,0x18,0x70,0x00,
+ 0x3b,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00,
+ 0x3c,0x66,0x60,0x66,0x3c,0x0c,0x06,0x3c,
+ 0x00,0x66,0x00,0x66,0x66,0x66,0x3f,0x00,
+ 0x1c,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,
+ 0x7e,0xc3,0x3c,0x06,0x3e,0x66,0x3f,0x00,
+ 0x66,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00,
+ 0x70,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00,
+ 0x18,0x18,0x3c,0x06,0x3e,0x66,0x3f,0x00,
+ 0x00,0x00,0x3c,0x60,0x60,0x3c,0x06,0x1c,
+ 0x7e,0xc3,0x3c,0x66,0x7e,0x60,0x3c,0x00,
+ 0xcc,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,
+ 0x70,0x00,0x3c,0x66,0x7e,0x60,0x3c,0x00,
+ 0x66,0x00,0x38,0x18,0x18,0x18,0x3c,0x00,
+ 0x3e,0x63,0x1c,0x0c,0x0c,0x0c,0x1e,0x00,
+ 0x70,0x00,0x38,0x18,0x18,0x18,0x3c,0x00,
+ 0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00,
+ 0x18,0x18,0x00,0x3c,0x66,0x7e,0x66,0x00,
+ 0x1c,0x00,0xfc,0x60,0x78,0x60,0xfc,0x00,
+ 0x00,0x00,0x7f,0x0c,0x7f,0xcc,0x7f,0x00,
+ 0x1f,0x36,0x66,0x7f,0x66,0x66,0x67,0x00,
+ 0x3c,0x66,0x00,0x3c,0x66,0x66,0x3c,0x00,
+ 0x00,0x66,0x00,0x3c,0x66,0x66,0x3c,0x00,
+ 0x00,0x70,0x00,0x3c,0x66,0x66,0x3c,0x00,
+ 0x3c,0x66,0x00,0x66,0x66,0x66,0x3f,0x00,
+ 0x00,0x70,0x00,0x66,0x66,0x66,0x3f,0x00,
+ 0x00,0xcc,0x00,0xcc,0xcc,0x7c,0x0c,0xf8,
+ 0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00,
+ 0x66,0x00,0x66,0x66,0x66,0x66,0x3c,0x00,
+ 0x0c,0x0c,0x3f,0x60,0x60,0x3f,0x0c,0x0c,
+ 0x1c,0x36,0x32,0x78,0x30,0x73,0x7e,0x00,
+ 0x66,0x66,0x3c,0x7e,0x18,0x7e,0x18,0x18,
+ 0xf8,0xcc,0xcc,0xfa,0xc6,0xcf,0xc6,0xc7,
+ 0x0e,0x1b,0x18,0x3c,0x18,0x18,0xd8,0x70,
+ 0x0e,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00,
+ 0x1c,0x00,0x38,0x18,0x18,0x18,0x3c,0x00,
+ 0x00,0x0e,0x00,0x3c,0x66,0x66,0x3c,0x00,
+ 0x00,0x0e,0x00,0x66,0x66,0x66,0x3f,0x00,
+ 0x00,0x7c,0x00,0x7c,0x66,0x66,0x66,0x00,
+ 0x7e,0x00,0x66,0x76,0x7e,0x6e,0x66,0x00,
+ 0x1e,0x36,0x36,0x1f,0x00,0x3f,0x00,0x00,
+ 0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00,
+ 0x18,0x00,0x18,0x30,0x60,0x66,0x3c,0x00,
+ 0x00,0x00,0x00,0x7e,0x60,0x60,0x00,0x00,
+ 0x00,0x00,0x00,0xfc,0x0c,0x0c,0x00,0x00,
+ 0xc3,0xc6,0xcc,0xde,0x33,0x66,0xcc,0x0f,
+ 0xc3,0xc6,0xcc,0xdb,0x37,0x6f,0xcf,0x03,
+ 0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00,
+ 0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00,
+ 0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00,
+ 0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,
+ 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,
+ 0xdb,0x77,0xdb,0xee,0xdb,0x77,0xdb,0xee,
+ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,
+ 0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,
+ 0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,
+ 0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,
+ 0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,
+ 0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,
+ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+ 0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,
+ 0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00,
+ 0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,
+ 0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,
+ 0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,
+ 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,
+ 0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,
+ 0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,
+ 0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,
+ 0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,
+ 0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,
+ 0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,
+ 0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,
+ 0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,
+ 0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,
+ 0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,
+ 0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,
+ 0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,
+ 0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,
+ 0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,
+ 0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,
+ 0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,
+ 0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,
+ 0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36,
+ 0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,
+ 0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+ 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x3b,0x6e,0x64,0x6e,0x3b,0x00,
+ 0x00,0x3c,0x66,0x7c,0x66,0x7c,0x60,0x60,
+ 0x00,0x7e,0x66,0x60,0x60,0x60,0x60,0x00,
+ 0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00,
+ 0x7e,0x66,0x30,0x18,0x30,0x66,0x7e,0x00,
+ 0x00,0x00,0x3f,0x6c,0x6c,0x6c,0x38,0x00,
+ 0x00,0x33,0x33,0x33,0x33,0x3e,0x30,0x60,
+ 0x00,0x3b,0x6e,0x0c,0x0c,0x0c,0x0c,0x00,
+ 0x7e,0x18,0x3c,0x66,0x66,0x3c,0x18,0x7e,
+ 0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00,
+ 0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00,
+ 0x0e,0x18,0x0c,0x3e,0x66,0x66,0x3c,0x00,
+ 0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00,
+ 0x06,0x0c,0x7e,0xdb,0xdb,0x7e,0x60,0xc0,
+ 0x1c,0x30,0x60,0x7c,0x60,0x30,0x1c,0x00,
+ 0x3c,0x66,0x66,0x66,0x66,0x66,0x66,0x00,
+ 0x00,0x7e,0x00,0x7e,0x00,0x7e,0x00,0x00,
+ 0x18,0x18,0x7e,0x18,0x18,0x00,0x7e,0x00,
+ 0x30,0x18,0x0c,0x18,0x30,0x00,0x7e,0x00,
+ 0x0c,0x18,0x30,0x18,0x0c,0x00,0x7e,0x00,
+ 0x0e,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,
+ 0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70,
+ 0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00,
+ 0x00,0x3b,0x6e,0x00,0x3b,0x6e,0x00,0x00,
+ 0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,
+ 0x0f,0x0c,0x0c,0x0c,0xec,0x6c,0x3c,0x1c,
+ 0x78,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,
+ 0x70,0x18,0x30,0x60,0x78,0x00,0x00,0x00,
+ 0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+};
+
diff --git a/src/target/firmware/display/ssd1783.c b/src/target/firmware/display/ssd1783.c
new file mode 100644
index 00000000..5696b48f
--- /dev/null
+++ b/src/target/firmware/display/ssd1783.c
@@ -0,0 +1,257 @@
+/* Solomon SSD1783 LCD Driver (Epson S1D15G10D08B000 clone) */
+
+/* (C) 2010 by Steve Markgraf <steve@steve-m.de>
+ * (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>
+//#define DEBUG
+#include <debug.h>
+#include <delay.h>
+#include <uwire.h>
+#include <display.h>
+#include <display/ssd1783.h>
+#include <calypso/clock.h>
+
+#define LCD_COLUMNS 98
+#define LCD_ROWS 67
+#define LCD_TOP_FREE_ROWS 3
+#define LCD_LEFT_FREE_COLS 0
+#define PIXEL_BYTES 3
+#define SSD1783_UWIRE_BITLEN 9
+#define SSD1783_DEV_ID 0
+#define FONT_HEIGHT 8
+#define FONT_WIDTH 8
+
+static const uint8_t rgb8_palette[] ={
+ 0x00, //P01 Intermediate red tone 000
+ 0x03, //P02 Intermediate red tone 001
+ 0x05, //P03 Intermediate red tone 010
+ 0x07, //P04 Intermediate red tone 011
+ 0x09, //P05 Intermediate red tone 100
+ 0x0b, //P06 Intermediate red tone 101
+ 0x0d, //P07 Intermediate red tone 110
+ 0x0f, //P08 Intermediate red tone 111
+ 0x00, //P09 Intermediate green tone 000
+ 0x03, //P10 Intermediate green tone 001
+ 0x05, //P11 Intermediate green tone 010
+ 0x07, //P12 Intermediate green tone 011
+ 0x09, //P13 Intermediate green tone 100
+ 0x0b, //P14 Intermediate green tone 101
+ 0x0d, //P15 Intermediate green tone 110
+ 0x0f, //P16 Intermediate green tone 111
+ 0x00, //P17 Intermediate blue tone 00
+ 0x05, //P18 Intermediate blue tone 01
+ 0x0a, //P19 Intermediate blue tone 10
+ 0x0f, //P20 Intermediate blue tone 11
+};
+
+static void ssd1783_cmd_write(const uint8_t cmd)
+{
+ uint16_t cmd_out = cmd;
+ uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &cmd_out, NULL);
+}
+
+static void ssd1783_data_write(const uint8_t data)
+{
+ uint16_t data_out = ((0x01 << 8) + data);
+ uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &data_out, NULL);
+}
+
+static void ssd1783_clrscr(void)
+{
+ uint16_t i;
+
+ /* Select the whole display area for clearing */
+ ssd1783_cmd_write(CMD_PASET); /* Page address set [2] */
+ ssd1783_data_write(0x00); /* Start page: 0x00 */
+ ssd1783_data_write(LCD_ROWS-1); /* End page */
+ ssd1783_cmd_write(CMD_CASET); /* Column address set [2] */
+ ssd1783_data_write(0x00); /* Start column: 0x00 */
+ ssd1783_data_write((LCD_COLUMNS/2)-1); /* End column (2 pixels per column) */
+ ssd1783_cmd_write(CMD_RAMWR); /* Write to memory */
+
+ /* Fill the display with white */
+ for(i=0; i < (LCD_ROWS * (LCD_COLUMNS/2) * PIXEL_BYTES); i++){
+ ssd1783_data_write(0xff);
+ }
+ ssd1783_cmd_write(CMD_NOP); /* Terminate RAMWR with NOP */
+}
+
+static void ssd1783_init(void)
+{
+ unsigned int i;
+
+ calypso_reset_set(RESET_EXT, 0);
+ uwire_init();
+ delay_ms(3);
+
+ /* Begin SSD1783 initialization sequence */
+ ssd1783_cmd_write(CMD_OSCON); /* Internal OSC on */
+ ssd1783_cmd_write(CMD_SLPOUT); /* Sleep out (Leave sleep mode) */
+
+ ssd1783_cmd_write(CMD_COMSCN); /* Common scan direction [1] */
+ ssd1783_data_write(0x01); /* Scan 1 -> 68, 132 <- 69 */
+ ssd1783_cmd_write(CMD_DATCTL); /* Data Scan Direction [3] */
+ ssd1783_data_write(0x00); /* Normal page address, normal rotation,
+ * scan direction in column direction */
+ ssd1783_data_write(0x00); /* RGB arrangement: RGB-RGB */
+ ssd1783_data_write(0x02); /* Gray-scale setup: 16 gray-scale Type A, 8-bit mode */
+
+ /* Initialize RGB8 palette for 8-Bit color mode */
+ ssd1783_cmd_write(CMD_RGBSET8); /* 256-color position set [20] */
+ for(i=0; i < sizeof(rgb8_palette); i++){
+ ssd1783_data_write(rgb8_palette[i]);
+ }
+
+ ssd1783_cmd_write(CMD_DISCTL); /* Display control [3] */
+ ssd1783_data_write(0xff); /* no clock division, F1, F2 switching period = field */
+ ssd1783_data_write(0x10); /* Drive duty, P24 = 1 */
+ ssd1783_data_write(0x01); /* FR inverse set, P30=1 */
+ ssd1783_cmd_write(CMD_SCSTART); /* Scroll start set [1] */
+ ssd1783_data_write(0x00); /* Start block address 0x00 */
+
+ /* Turn on the power regulator which generates VLCD */
+ ssd1783_cmd_write(CMD_PWRCTR); /* Power Control [1] */
+ ssd1783_data_write(0x0b); /* Booster, follower and regulator circuit on */
+
+ /* FIXME: put this in a separate function (ssd1783_set_contrast) */
+ ssd1783_cmd_write(CMD_VOLCTR); /* Electronic Volume Control [2] */
+ ssd1783_data_write(0x29); /* Set contrast */
+ ssd1783_data_write(0x05); /* Set contrast */
+
+ ssd1783_cmd_write(CMD_DISINV); /* Invert Display */
+ ssd1783_cmd_write(CMD_TMPGRD); /* Temperature gradient set */
+ ssd1783_data_write(0x00); /* default temperature gradient (-0.05% / °C) */
+ ssd1783_cmd_write(CMD_BIASSET); /* Set biasing ratio [1] */
+ ssd1783_data_write(0x03); /* 1/10 bias */
+ ssd1783_cmd_write(CMD_FREQSET); /* Set frequency and n-line inversion [2] */
+ ssd1783_data_write(0x08); /* frequency: 75Hz (POR) */
+ ssd1783_data_write(0x06); /* n-line inversion: 6 lines */
+ ssd1783_cmd_write(CMD_RESCMD); /* reserved command in datasheet? */
+ ssd1783_cmd_write(CMD_PWMSEL); /* Select PWM/FRC, Full/8 color mode [3] */
+ ssd1783_data_write(0x28); /* fixed */
+ ssd1783_data_write(0x2c); /* 5 bits PWM + 1 bit FRC (POR) */
+ ssd1783_data_write(0x05); /* Full color mode (0x45 would be 8 color powersaving) */
+
+ ssd1783_cmd_write(CMD_DISON); /* Display ON */
+ ssd1783_clrscr(); /* Clear the display */
+}
+
+extern const unsigned char fontdata_r8x8_horiz[];
+
+/*
+ * Pixel format for 8-bit mode, 12-bit color, 2 Pixel per 3 byte
+ * D7, D6, D5, D4, D3, D2, D1, D0: RRRRGGGG (8 bits) 1st write
+ * D7, D6, D5, D4, D3, D2, D1, D0: BBBBRRRR (8 bits) 2nd write
+ * D7, D6, D5, D4, D3, D2, D1, D0: GGGGBBBB (8 bits) 3rd write
+*/
+
+static void ssd1783_goto_xy(int xpos, int ypos)
+{
+ ssd1783_cmd_write(CMD_PASET);
+ ssd1783_data_write(xpos);
+ ssd1783_data_write(xpos + (FONT_HEIGHT-1));
+
+ ssd1783_cmd_write(CMD_CASET);
+ ssd1783_data_write(ypos);
+ ssd1783_data_write(ypos + ((FONT_WIDTH/2)-1));
+
+ ssd1783_cmd_write(CMD_NOP);
+}
+
+static int ssd1783_putc_col(unsigned char c, int fColor, int bColor)
+{
+ int i, j;
+ uint8_t cols = FONT_WIDTH;
+ uint8_t rows = FONT_HEIGHT;
+ uint8_t row_slice;
+ uint8_t rowmask;
+ uint16_t pixel0; /* left pixel */
+ uint16_t pixel1; /* right pixel */
+
+ ssd1783_cmd_write(CMD_RAMWR);
+
+ for (i = 0; i < rows; i++) {
+ row_slice = fontdata_r8x8_horiz[(FONT_WIDTH * c)+i];
+ printd("\nSSD1783 FontData=0x%02hx", row_slice);
+ rowmask = 0x80;
+ for (j = 0; j < cols; j += 2) {
+ if (!(row_slice & rowmask))
+ pixel0 = bColor;
+ else
+ pixel0 = fColor;
+ rowmask = rowmask >> 1;
+ if (!(row_slice & rowmask))
+ pixel1 = bColor;
+ else
+ pixel1 = fColor;
+ rowmask = rowmask >> 1;
+ /* Write the RGB-RGB pixel data */
+ ssd1783_data_write((pixel0 >> 4) & 0xff);
+ ssd1783_data_write(((pixel0 & 0x00f) << 4) | ((pixel1 >> 8) & 0x00f));
+ ssd1783_data_write(pixel1 & 0xff);
+ }
+ }
+ ssd1783_cmd_write(CMD_NOP);
+
+ return c;
+}
+
+static int ssd1783_puts_col(const char *str, int txtline, int fColor, int bColor)
+{
+ int i;
+ for (i = 0; *str != 0x00; i += (FONT_WIDTH/2)) {
+ ssd1783_goto_xy(((txtline*FONT_HEIGHT)+LCD_TOP_FREE_ROWS),
+ (i + LCD_LEFT_FREE_COLS));
+ ssd1783_putc_col(*str++, fColor, bColor);
+ }
+
+ return 0;
+}
+
+/* interface to display driver core */
+
+static void ssd1783_set_attr(unsigned long attr)
+{
+ /* FIXME */
+}
+
+static int ssd1783_putc(unsigned int c)
+{
+ return ssd1783_putc_col(c, BLACK, WHITE);
+}
+
+static int ssd1783_puts(const char *str)
+{
+ return ssd1783_puts_col(str, 0, BLACK, WHITE);
+}
+
+const struct display_driver ssd1783_display = {
+ .name = "ssd1783",
+ .init = &ssd1783_init,
+ .set_attr = &ssd1783_set_attr,
+ .unset_attr = &ssd1783_set_attr,
+ .clrscr = &ssd1783_clrscr,
+ .goto_xy = &ssd1783_goto_xy,
+ .putc = &ssd1783_putc,
+ .puts = &ssd1783_puts,
+};
diff --git a/src/target/firmware/display/st7558.c b/src/target/firmware/display/st7558.c
new file mode 100644
index 00000000..baed9eb0
--- /dev/null
+++ b/src/target/firmware/display/st7558.c
@@ -0,0 +1,123 @@
+/* Sitronix ST7558 LCD Driver */
+
+/* (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 <delay.h>
+#include <memory.h>
+#include <i2c.h>
+#include <display.h>
+#include <calypso/clock.h>
+
+#define MORE_CONTROL 0x80
+#define CONTROL_RS_RAM 0x40
+#define CONTROL_RS_CMD 0x00
+#define Y_ADDR(n) (0x40|((n)&0xf))
+#define X_ADDR(n) (0x80|((n)&0x3f))
+
+static const uint8_t setup[] = { CONTROL_RS_CMD, 0x2e, 0x21, 0x12, 0xc0, 0x0b, 0x20, 0x11, 0x00, 0x40, 0x80 };
+static const uint8_t home[] = { CONTROL_RS_CMD, Y_ADDR(0), X_ADDR(0) };
+
+/* video modes */
+static const uint8_t invert[] = { CONTROL_RS_CMD, 0x20, 0x0d };
+static const uint8_t normal[] = { CONTROL_RS_CMD, 0x20, 0x0c };
+static const uint8_t off[] = { CONTROL_RS_CMD, 0x20, 0x08 };
+
+#define ST7558_SLAVE_ADDR 0x3c
+static int st7558_write(const uint8_t *data, int len)
+{
+ int rc = i2c_write(ST7558_SLAVE_ADDR, data[0], 1, data+1, len-1);
+ /* FIXME: find out why this is needed! */
+ delay_ms(10);
+ return rc;
+}
+
+static const uint8_t zero16[] = { CONTROL_RS_RAM,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+static void st7558_clrscr(void)
+{
+ int i;
+
+ st7558_write(home, sizeof(home));
+
+ for (i = 0; i < 102*9; i += 16)
+ st7558_write(zero16, sizeof(zero16));
+
+ st7558_write(home, sizeof(home));
+}
+
+static void st7558_init(void)
+{
+ /* Release nRESET */
+ calypso_reset_set(RESET_EXT, 0);
+
+ i2c_init(0,0);
+ delay_ms(10);
+
+ st7558_write(setup, sizeof(setup));
+ st7558_clrscr();
+}
+
+static void st7558_set_attr(unsigned long attr)
+{
+ if (attr & DISP_ATTR_INVERT)
+ st7558_write(invert, sizeof(invert));
+}
+
+static void st7558_unset_attr(unsigned long attr)
+{
+ if (attr & DISP_ATTR_INVERT)
+ st7558_write(normal, sizeof(normal));
+}
+
+/* FIXME: we need a mini-libc */
+static void *mcpy(uint8_t *dst, const uint8_t *src, int len)
+{
+ while (len--)
+ *dst++ = *src++;
+
+ return dst;
+}
+
+extern const unsigned char fontdata_r8x8[];
+
+static void st7558_putc(unsigned char c)
+{
+ uint8_t putc_buf[16];
+ uint8_t bytes_per_char = 8;
+
+ putc_buf[0] = CONTROL_RS_RAM;
+ mcpy(putc_buf+1, fontdata_r8x8+(c*bytes_per_char), bytes_per_char);
+ st7558_write(putc_buf, 1+bytes_per_char);
+}
+
+const struct display_driver st7558_display = {
+ .name = "st7558",
+ .init = &st7558_init,
+ .clrscr = &st7558_clrscr,
+ .set_attr = &st7558_set_attr,
+ .unset_attr = &st7558_unset_attr,
+ .putc = &st7558_putc,
+};
diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c
new file mode 100644
index 00000000..a269142f
--- /dev/null
+++ b/src/target/firmware/flash/cfi_flash.c
@@ -0,0 +1,436 @@
+/* NOR Flash Driver for Intel 28F160C3 NOR flash */
+
+/* (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 <debug.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <memory.h>
+#include <cfi_flash.h>
+
+/* XXX: memdump_range() */
+#include <calypso/misc.h>
+
+enum flash_cmd {
+ FLASH_CMD_RESET = 0xff,
+ FLASH_CMD_READ_ID = 0x90,
+ FLASH_CMD_CFI = 0x98,
+ FLASH_CMD_READ_STATUS = 0x70,
+ FLASH_CMD_CLEAR_STATUS = 0x50,
+ FLASH_CMD_WRITE = 0x40,
+ FLASH_CMD_BLOCK_ERASE = 0x20,
+ FLASH_CMD_ERASE_CONFIRM = 0xD0,
+ FLASH_CMD_PROTECT = 0x60,
+};
+
+enum flash_prot_cmd {
+ FLASH_PROT_LOCK = 0x01,
+ FLASH_PROT_UNLOCK = 0xD0,
+ FLASH_PROT_LOCKDOWN = 0x2F
+};
+
+enum flash_offset {
+ FLASH_OFFSET_MANUFACTURER_ID = 0x00,
+ FLASH_OFFSET_DEVICE_ID = 0x01,
+ FLASH_OFFSET_INTEL_PROTECTION = 0x81,
+ FLASH_OFFSET_CFI_RESP = 0x10
+};
+
+enum flash_block_offset {
+ FLASH_OFFSET_BLOCK_LOCKSTATE = 0x02
+};
+
+enum flash_status {
+ FLASH_STATUS_READY = 0x80,
+ FLASH_STATUS_ERASE_SUSPENDED = 0x40,
+ FLASH_STATUS_ERASE_ERROR = 0x20,
+ FLASH_STATUS_PROGRAM_ERROR = 0x10,
+ FLASH_STATUS_VPP_LOW = 0x08,
+ FLASH_STATUS_PROGRAM_SUSPENDED = 0x04,
+ FLASH_STATUS_LOCKED_ERROR = 0x02,
+ FLASH_STATUS_RESERVED = 0x01
+};
+
+static inline void flash_write_cmd(const void *base_addr, uint16_t cmd)
+{
+ writew(cmd, base_addr);
+}
+
+static inline uint16_t flash_read16(const void *base_addr, uint32_t offset)
+{
+ return readw(base_addr + (offset << 1));
+}
+
+static char flash_protected(uint32_t block_offset) {
+ return block_offset < 64*1024;
+}
+
+uint8_t flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset) {
+ const void *base_addr = flash->f_base;
+ uint8_t lockstate;
+ flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+ lockstate = flash_read16(base_addr, block_offset + FLASH_OFFSET_BLOCK_LOCKSTATE);
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+ return lockstate;
+}
+
+void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset) {
+ const void *base_addr = flash->f_base;
+ printf("Unlocking block at 0x%08x\n", block_offset);
+
+ if(flash_protected(block_offset)) {
+ puts("error: block is soft-protected\n");
+ return;
+ }
+
+ flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
+ flash_write_cmd(base_addr + block_offset, FLASH_PROT_UNLOCK);
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset) {
+ const void *base_addr = flash->f_base;
+ printf("Locking block at 0x%08x\n", block_offset);
+ flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
+ flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCK);
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset) {
+ const void *base_addr = flash->f_base;
+ printf("Locking down block at 0x%08x\n", block_offset);
+ flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
+ flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCKDOWN);
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+void flash_block_erase(cfi_flash_t *flash, uint32_t block_offset) {
+ const void *base_addr = flash->f_base;
+ printf("Erasing block 0x%08x...", block_offset);
+
+ if(flash_protected(block_offset)) {
+ puts("error: block is soft-protected\n");
+ return;
+ }
+
+ void *block_addr = ((uint8_t*)base_addr) + block_offset;
+
+ flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS);
+
+ flash_write_cmd(block_addr, FLASH_CMD_BLOCK_ERASE);
+ flash_write_cmd(block_addr, FLASH_CMD_ERASE_CONFIRM);
+
+ flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS);
+ uint16_t status;
+ do {
+ status = flash_read16(base_addr, 0);
+ } while(!(status&FLASH_STATUS_READY));
+
+ if(status&FLASH_STATUS_ERASE_ERROR) {
+ puts("error: ");
+ if(status&FLASH_STATUS_VPP_LOW) {
+ puts("vpp insufficient\n");
+ }
+ if(status&FLASH_STATUS_LOCKED_ERROR) {
+ puts("block is lock-protected\n");
+ }
+ } else {
+ puts("done\n");
+ }
+
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) {
+ const void *base_addr = flash->f_base;
+ uint32_t i;
+
+ printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src);
+
+ if(dst%2) {
+ puts("error: unaligned destination\n");
+ return;
+ }
+
+ if(nbytes%2) {
+ puts("error: unaligned count\n");
+ return;
+ }
+
+ if(flash_protected(dst)) {
+ puts("error: block is soft-protected\n");
+ return;
+ }
+
+ flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS);
+
+ puts("writing...");
+ for(i = 0; i < nbytes; i += 2) {
+ uint16_t *src_addr = (uint16_t*)(src + i);
+ uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i);
+
+ uint16_t data = *src_addr;
+
+ flash_write_cmd(dst_addr, FLASH_CMD_WRITE);
+ flash_write_cmd(dst_addr, data);
+
+ flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS);
+ uint16_t status;
+ do {
+ status = flash_read16(base_addr, 0);
+ } while(!(status&FLASH_STATUS_READY));
+
+ if(status&FLASH_STATUS_PROGRAM_ERROR) {
+ puts("error: ");
+ if(status&FLASH_STATUS_VPP_LOW) {
+ puts("vpp insufficient");
+ }
+ if(status&FLASH_STATUS_LOCKED_ERROR) {
+ puts("block is lock-protected");
+ }
+ goto err_reset;
+ }
+ }
+
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+
+ puts("verifying...");
+ for(i = 0; i < nbytes; i += 2) {
+ uint16_t *src_addr = (uint16_t*)(src + i);
+ uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i);
+ if(*src_addr != *dst_addr) {
+ puts("error: verification failed");
+ goto err;
+ }
+ }
+
+ puts("done\n");
+
+ return;
+
+ err_reset:
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+
+ err:
+ printf(" at offset 0x%x\n", i);
+}
+
+typedef void (*flash_block_cb_t)(cfi_flash_t *flash,
+ uint32_t block_offset,
+ uint32_t block_size);
+
+void flash_iterate_blocks(cfi_flash_t *flash, struct cfi_query *qry,
+ uint32_t start_offset, uint32_t end_offset,
+ flash_block_cb_t callback)
+{
+ int region, block;
+
+ uint32_t block_start = 0;
+ for(region = 0; region < qry->num_erase_regions; region++) {
+ uint16_t actual_count = qry->erase_regions[region].b_count + 1;
+ uint32_t actual_size = qry->erase_regions[region].b_size * 256;
+ for(block = 0; block < actual_count; block++) {
+ uint32_t block_end = block_start + actual_size;
+ if(block_start >= start_offset && block_end-1 <= end_offset) {
+ callback(flash, block_start, actual_size);
+ }
+ block_start = block_end;
+ }
+ }
+}
+
+static void get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) {
+ flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+
+ *manufacturer_id = flash_read16(base_addr, FLASH_OFFSET_MANUFACTURER_ID);
+ *device_id = flash_read16(base_addr, FLASH_OFFSET_DEVICE_ID);
+
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+static void get_query(void *base_addr, struct cfi_query *query) {
+ unsigned int i;
+
+ flash_write_cmd(base_addr, FLASH_CMD_CFI);
+
+ for(i = 0; i < sizeof(struct cfi_query); i++) {
+ uint16_t byte = flash_read16(base_addr, FLASH_OFFSET_CFI_RESP+i);
+ *(((unsigned char*)query)+i) = byte;
+ }
+
+ if(query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') {
+ puts("Error: CFI query signature not found\n");
+ }
+
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+static void dump_query(void *base_addr, struct cfi_query *query) {
+ unsigned int i;
+
+ flash_write_cmd(base_addr, FLASH_CMD_CFI);
+
+ for(i = 0; i < sizeof(struct cfi_query); i++) {
+ uint8_t byte = *(((uint8_t*)query)+i);
+ printf("%04X: %02X\n", FLASH_OFFSET_CFI_RESP+i, byte);
+ }
+
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+static void dump_layout(void *base_addr, const struct cfi_query *qry) {
+ int region;
+
+ flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+ for(region = 0; region < qry->num_erase_regions; region++) {
+ uint16_t actual_count = qry->erase_regions[region].b_count + 1;
+ uint32_t actual_size = qry->erase_regions[region].b_size * 256;
+ printf("Region of 0x%04x times 0x%6x bytes\n", actual_count,
+ actual_size);
+ }
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+static void dump_locks(void *base_addr, const struct cfi_query *qry) {
+ int region, block;
+
+ uint32_t block_addr = 0;
+ flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+ for(region = 0; region < qry->num_erase_regions; region++) {
+ uint16_t actual_count = qry->erase_regions[region].b_count + 1;
+ uint32_t actual_size = qry->erase_regions[region].b_size * 256;
+ for(block = 0; block < actual_count; block++) {
+ uint8_t lock = flash_read16(base_addr, block_addr+2);
+ printf("Block 0x%08x lock 0x%02x\n", block_addr*2, lock);
+ block_addr += actual_size / 2;
+ }
+ }
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+static void dump_protection(void *base_addr) {
+ flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+
+ uint16_t lock = flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION);
+ printf("Protection Lock: 0x%04x\n", lock);
+
+ puts("Protection Data: ");
+ int i;
+ for(i = 0; i < 8; i++) {
+ printf("%04x", flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION + 1 + i));
+ }
+ putchar('\n');
+
+ flash_write_cmd(base_addr, FLASH_CMD_RESET);
+}
+
+static void dump_timing(void *base_addr, struct cfi_query *qry) {
+ uint32_t block_erase_typ = 1<<qry->block_erase_timeout_typ;
+ uint32_t block_erase_max = (1<<qry->block_erase_timeout_max) * block_erase_typ;
+ uint32_t word_program_typ = 1<<qry->word_write_timeout_typ;
+ uint32_t word_program_max = (1<<qry->word_write_timeout_max) * word_program_typ;
+ printf("Block Erase Typical: %u ms\n", block_erase_typ);
+ printf("Block Erase Maximum: %u ms\n", block_erase_max);
+ printf("Word Program Typical: %u us\n", word_program_typ);
+ printf("Word Program Maximum: %u us\n", word_program_max);
+}
+
+static void dump_algorithms(void *base_addr, struct cfi_query *qry) {
+ printf("Primary Algorithm ID: %04x\n", qry->p_id);
+ printf("Primary Extended Query: %04x\n", qry->p_adr);
+
+ printf("Alternate Algorithm ID: %04x\n", qry->a_id);
+ printf("Alternate Extended Query: %04x\n", qry->a_adr);
+}
+
+void
+lockdown_block_cb(cfi_flash_t *flash,
+ uint32_t block_offset,
+ uint32_t block_size)
+{
+ flash_block_lockdown(flash, block_offset);
+}
+
+void
+print_block_cb(cfi_flash_t *flash,
+ uint32_t block_offset,
+ uint32_t block_size)
+{
+ printf("%08x size %08x\n", block_offset, block_size);
+}
+
+void flash_dump_info(cfi_flash_t *flash) {
+ void *base_addr = flash->f_base;
+ struct cfi_query *qry = &flash->f_query;
+
+ printf("Flash Manufacturer ID: %04x\n", flash->f_manuf_id);
+ printf("Flash Device ID: %04x\n", flash->f_dev_id);
+
+ printf("Flash Size: 0x%08x bytes\n", flash->f_size);
+
+ dump_algorithms(base_addr, qry);
+
+ dump_timing(base_addr, qry);
+
+ dump_protection(base_addr);
+
+ dump_layout(base_addr, qry);
+
+ dump_locks(base_addr, qry);
+}
+
+void flash_init(cfi_flash_t *flash, void *base_addr) {
+ printd("Initializing CFI flash at 0x%p\n", base_addr);
+
+ flash->f_base = base_addr;
+
+ get_id(base_addr, &flash->f_manuf_id, &flash->f_dev_id);
+
+ get_query(base_addr, &flash->f_query);
+
+ flash->f_size = 1<<flash->f_query.dev_size;
+}
+
+void flash_test() {
+ /* block iterator test */
+#if 0
+ flash_iterate_blocks(flash, qry, 0x0000, 0xFFFF, &lockdown_block_cb);
+#endif
+
+ /* programming test */
+#if 0
+ static uint8_t magic[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF};
+
+ memdump_range(&magic, sizeof(magic));
+
+#if 0
+#define ADDR 0x001E0000
+ flash_block_unlock(flash, ADDR);
+ memdump_range(ADDR, 16);
+ flash_block_erase(flash, ADDR);
+ memdump_range(ADDR, 16);
+ flash_program(flash, ADDR, &magic, sizeof(magic));
+ memdump_range(ADDR, 16);
+#undef ADDR
+#endif
+
+#endif
+}
diff --git a/src/target/firmware/include/abb/twl3025.h b/src/target/firmware/include/abb/twl3025.h
new file mode 100644
index 00000000..2cd35a58
--- /dev/null
+++ b/src/target/firmware/include/abb/twl3025.h
@@ -0,0 +1,136 @@
+#ifndef _TWL3025_H
+#define _TWL3025_H
+
+#define PAGE(n) (n << 7)
+enum twl3025_reg {
+ VRPCCFG = PAGE(1) | 30,
+ VRPCDEV = PAGE(0) | 30,
+ VRPCMSK = PAGE(1) | 31,
+ VRPCMSKABB = PAGE(1) | 29,
+ VRPCSTS = PAGE(0) | 31,
+ /* Monitoring ADC Registers */
+ MADCTRL = PAGE(0) | 13,
+ MADCSTAT = PAGE(0) | 24,
+ VBATREG = PAGE(0) | 15,
+ VCHGREG = PAGE(0) | 16,
+ ICHGREG = PAGE(0) | 17,
+ VBKPREG = PAGE(0) | 18,
+ ADIN1REG = PAGE(0) | 19,
+ ADIN2REG = PAGE(0) | 20,
+ ADIN3REG = PAGE(0) | 21,
+ ADIN4REG = PAGE(0) | 22,
+ /* Clock Generator Registers */
+ TOGBR1 = PAGE(0) | 4,
+ TOGBR2 = PAGE(0) | 5,
+ PWDNRG = PAGE(1) | 9,
+ TAPCTRL = PAGE(1) | 19,
+ TAPREG = PAGE(1) | 20,
+ /* Automatic Frequency Control (AFC) Registers */
+ AUXAFC1 = PAGE(0) | 7,
+ AUXAFC2 = PAGE(0) | 8,
+ AFCCTLADD = PAGE(1) | 21,
+ AFCOUT = PAGE(1) | 22,
+ /* Automatic Power Control (APC) Registers */
+ APCDEL1 = PAGE(0) | 2,
+ APCDEL2 = PAGE(1) | 26,
+ AUXAPC = PAGE(0) | 9,
+ APCRAM = PAGE(0) | 10,
+ APCOFF = PAGE(0) | 11,
+ APCOUT = PAGE(1) | 12,
+ /* Auxiliary DAC Control Register */
+ AUXDAC = PAGE(0) | 12,
+ /* SimCard Control Register */
+ VRPCSIM = PAGE(1) | 23,
+ /* LED Driver Register */
+ AUXLED = PAGE(1) | 24,
+ /* Battery Charger Interface (BCI) Registers */
+ CHGREG = PAGE(0) | 25,
+ BCICTL1 = PAGE(0) | 28,
+ BCICTL2 = PAGE(0) | 29,
+ BCICONF = PAGE(1) | 13,
+ /* Interrupt and Bus Control (IBIC) Registers */
+ ITMASK = PAGE(0) | 28,
+ ITSTATREG = PAGE(0) | 27, /* both pages! */
+ PAGEREG = PAGE(0) | 1, /* both pages! */
+ /* Baseband Codec (BBC) Registers */
+ BULIOFF = PAGE(1) | 2,
+ BULQOFF = PAGE(1) | 3,
+ BULIDAC = PAGE(1) | 5,
+ BULQDAC = PAGE(1) | 4,
+ BULGCAL = PAGE(1) | 14,
+ BULDATA1 = PAGE(0) | 3, /* 16 words */
+ BBCTRL = PAGE(1) | 6,
+ /* Voiceband Codec (VBC) Registers */
+ VBCTRL1 = PAGE(1) | 8,
+ VBCTRL2 = PAGE(1) | 11,
+ VBPOP = PAGE(1) | 10,
+ VBUCTRL = PAGE(1) | 7,
+ VBDCTRL = PAGE(0) | 6,
+};
+#define BULDATA2 BULDATA1
+
+enum togbr2_bits {
+ TOGBR2_KEEPR = (1 << 0), /* Clear KEEPON bit */
+ TOGBR2_KEEPS = (1 << 1), /* Set KEEPON bit */
+ TOGBR2_ACTR = (1 << 2), /* Dectivate MCLK */
+ TOGBR2_ACTS = (1 << 3), /* Activate MCLK */
+ TOGBR2_IBUFPTR1 = (1 << 4), /* Initialize pointer of burst buffer 1 */
+ TOGBR2_IBUFPTR2 = (1 << 5), /* Initialize pointer of burst buffer 2 */
+ TOGBR2_IAPCPTR = (1 << 6), /* Initialize pointer of APC RAM */
+};
+
+/* How a RAMP value is encoded */
+#define ABB_RAMP_VAL(up, down) ( ((down & 0x1F) << 5) | (up & 0x1F) )
+
+enum twl3025_unit {
+ TWL3025_UNIT_AFC,
+ TWL3025_UNIT_MAD,
+ TWL3025_UNIT_ADA,
+ TWL3025_UNIT_VDL,
+ TWL3025_UNIT_VUL,
+};
+
+void twl3025_init(void);
+void twl3025_reg_write(uint8_t reg, uint16_t data);
+uint16_t twl3025_reg_read(uint8_t reg);
+
+void twl3025_power_off(void);
+
+void twl3025_clk13m(int enable);
+
+void twl3025_unit_enable(enum twl3025_unit unit, int on);
+
+enum twl3025_tsp_bits {
+ BULON = 0x80,
+ BULCAL = 0x40,
+ BULENA = 0x20,
+ BDLON = 0x10,
+ BDLCAL = 0x08,
+ BDLENA = 0x04,
+ STARTADC = 0x02,
+};
+
+extern const uint16_t twl3025_default_ramp[16];
+
+/* Enqueue a TSP signal change via the TPU */
+void twl3025_tsp_write(uint8_t data);
+
+/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */
+void twl3025_downlink(int on, int16_t at);
+
+/* Enqueue a series of TSP commands in the TPU to (de)activate the uplink path */
+void twl3025_uplink(int on, int16_t at);
+
+/* Update the AFC DAC value */
+void twl3025_afc_set(int16_t val);
+
+/* Get the AFC DAC value */
+int16_t twl3025_afc_get(void);
+
+/* Get the AFC DAC output value */
+uint8_t twl3025_afcout_get(void);
+
+/* Force a certain static AFC DAC output value */
+void twl3025_afcout_set(uint8_t val);
+
+#endif
diff --git a/src/target/firmware/include/arm.h b/src/target/firmware/include/arm.h
new file mode 100644
index 00000000..272c9c39
--- /dev/null
+++ b/src/target/firmware/include/arm.h
@@ -0,0 +1,7 @@
+#ifndef _ARM_H
+#define _ARM_H
+
+void arm_enable_interrupts(void);
+int arm_disable_interrupts(void);
+
+#endif
diff --git a/src/target/firmware/include/arpa/inet.h b/src/target/firmware/include/arpa/inet.h
new file mode 100644
index 00000000..9a4dd5c9
--- /dev/null
+++ b/src/target/firmware/include/arpa/inet.h
@@ -0,0 +1,2 @@
+/* we have this to make sure libosmocore uses our version of ntohl/htons */
+#include <byteorder.h>
diff --git a/src/target/firmware/include/asm/assembler.h b/src/target/firmware/include/asm/assembler.h
new file mode 100644
index 00000000..cd03e98d
--- /dev/null
+++ b/src/target/firmware/include/asm/assembler.h
@@ -0,0 +1,113 @@
+/*
+ * linux/include/asm-arm/assembler.h
+ *
+ * Copyright (C) 1996-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains arm architecture specific defines
+ * for the different processors.
+ *
+ * Do not include any C declarations in this file - it is included by
+ * assembler source.
+ */
+#ifndef __ASSEMBLY__
+#error "Only include this from assembly code"
+#endif
+
+#include <asm/ptrace.h>
+
+/*
+ * Endian independent macros for shifting bytes within registers.
+ */
+#ifndef __ARMEB__
+#define pull lsr
+#define push lsl
+#define get_byte_0 lsl #0
+#define get_byte_1 lsr #8
+#define get_byte_2 lsr #16
+#define get_byte_3 lsr #24
+#define put_byte_0 lsl #0
+#define put_byte_1 lsl #8
+#define put_byte_2 lsl #16
+#define put_byte_3 lsl #24
+#else
+#define pull lsl
+#define push lsr
+#define get_byte_0 lsr #24
+#define get_byte_1 lsr #16
+#define get_byte_2 lsr #8
+#define get_byte_3 lsl #0
+#define put_byte_0 lsl #24
+#define put_byte_1 lsl #16
+#define put_byte_2 lsl #8
+#define put_byte_3 lsl #0
+#endif
+
+#define PLD(code...)
+
+#define MODE_USR USR_MODE
+#define MODE_FIQ FIQ_MODE
+#define MODE_IRQ IRQ_MODE
+#define MODE_SVC SVC_MODE
+
+#define DEFAULT_FIQ MODE_FIQ
+
+/*
+ * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc})
+ */
+#ifdef __STDC__
+#define LOADREGS(cond, base, reglist...)\
+ ldm##cond base,reglist
+#else
+#define LOADREGS(cond, base, reglist...)\
+ ldm/**/cond base,reglist
+#endif
+
+/*
+ * Build a return instruction for this processor type.
+ */
+#define RETINSTR(instr, regs...)\
+ instr regs
+
+/*
+ * Enable and disable interrupts
+ */
+ .macro disable_irq
+ msr cpsr_c, #PSR_I_BIT | SVC_MODE
+ .endm
+
+ .macro enable_irq
+ msr cpsr_c, #SVC_MODE
+ .endm
+
+/*
+ * Save the current IRQ state and disable IRQs. Note that this macro
+ * assumes FIQs are enabled, and that the processor is in SVC mode.
+ */
+ .macro save_and_disable_irqs, oldcpsr
+ mrs \oldcpsr, cpsr
+ disable_irq
+ .endm
+
+/*
+ * Restore interrupt state previously stored in a register. We don't
+ * guarantee that this will preserve the flags.
+ */
+ .macro restore_irqs, oldcpsr
+ msr cpsr_c, \oldcpsr
+ .endm
+
+/*
+ * These two are used to save LR/restore PC over a user-based access.
+ * The old 26-bit architecture requires that we do. On 32-bit
+ * architecture, we can safely ignore this requirement.
+ */
+ .macro save_lr
+ .endm
+
+ .macro restore_pc
+ mov pc, lr
+ .endm
diff --git a/src/target/firmware/include/asm/atomic.h b/src/target/firmware/include/asm/atomic.h
new file mode 100644
index 00000000..19e8ce6f
--- /dev/null
+++ b/src/target/firmware/include/asm/atomic.h
@@ -0,0 +1,106 @@
+/*
+ * linux/include/asm-arm/atomic.h
+ *
+ * Copyright (C) 1996 Russell King.
+ * Copyright (C) 2002 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ASM_ARM_ATOMIC_H
+#define __ASM_ARM_ATOMIC_H
+
+typedef struct { volatile int counter; } atomic_t;
+
+#define ATOMIC_INIT(i) { (i) }
+
+#define atomic_read(v) ((v)->counter)
+
+#include <asm/system.h>
+#include <asm/compiler.h>
+
+#define atomic_set(v,i) (((v)->counter) = (i))
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+ unsigned long flags;
+ int val;
+
+ local_irq_save(flags);
+ val = v->counter;
+ v->counter = val += i;
+ local_irq_restore(flags);
+
+ return val;
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+ unsigned long flags;
+ int val;
+
+ local_irq_save(flags);
+ val = v->counter;
+ v->counter = val -= i;
+ local_irq_restore(flags);
+
+ return val;
+}
+
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+ int ret;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ ret = v->counter;
+ if (likely(ret == old))
+ v->counter = new;
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ *addr &= ~mask;
+ local_irq_restore(flags);
+}
+
+#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+
+static inline int atomic_add_unless(atomic_t *v, int a, int u)
+{
+ int c, old;
+
+ c = atomic_read(v);
+ while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
+ c = old;
+ return c != u;
+}
+#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
+
+#define atomic_add(i, v) (void) atomic_add_return(i, v)
+#define atomic_inc(v) (void) atomic_add_return(1, v)
+#define atomic_sub(i, v) (void) atomic_sub_return(i, v)
+#define atomic_dec(v) (void) atomic_sub_return(1, v)
+
+#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
+#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
+#define atomic_inc_return(v) (atomic_add_return(1, v))
+#define atomic_dec_return(v) (atomic_sub_return(1, v))
+#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
+
+#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
+
+/* Atomic operations are already serializing on ARM */
+#define smp_mb__before_atomic_dec() barrier()
+#define smp_mb__after_atomic_dec() barrier()
+#define smp_mb__before_atomic_inc() barrier()
+#define smp_mb__after_atomic_inc() barrier()
+
+#endif
diff --git a/src/target/firmware/include/asm/bitops.h b/src/target/firmware/include/asm/bitops.h
new file mode 100644
index 00000000..337d800d
--- /dev/null
+++ b/src/target/firmware/include/asm/bitops.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright 1995, Russell King.
+ * Various bits and pieces copyrights include:
+ * Linus Torvalds (test_bit).
+ * Big endian support: Copyright 2001, Nicolas Pitre
+ * reworked by rmk.
+ *
+ * bit 0 is the LSB of an "unsigned long" quantity.
+ *
+ * Please note that the code in this file should never be included
+ * from user space. Many of these are not implemented in assembler
+ * since they would be too costly. Also, they require privileged
+ * instructions (which are not available from user mode) to ensure
+ * that they are atomic.
+ */
+
+#ifndef __ASM_ARM_BITOPS_H
+#define __ASM_ARM_BITOPS_H
+
+#include <asm/system.h>
+
+#define smp_mb__before_clear_bit() mb()
+#define smp_mb__after_clear_bit() mb()
+
+/*
+ * These functions are the basis of our bit ops.
+ *
+ * First, the atomic bitops. These use native endian.
+ */
+static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
+{
+ unsigned long flags;
+ unsigned long mask = 1UL << (bit & 31);
+
+ p += bit >> 5;
+
+ local_irq_save(flags);
+ *p |= mask;
+ local_irq_restore(flags);
+}
+
+static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p)
+{
+ unsigned long flags;
+ unsigned long mask = 1UL << (bit & 31);
+
+ p += bit >> 5;
+
+ local_irq_save(flags);
+ *p &= ~mask;
+ local_irq_restore(flags);
+}
+
+static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p)
+{
+ unsigned long flags;
+ unsigned long mask = 1UL << (bit & 31);
+
+ p += bit >> 5;
+
+ local_irq_save(flags);
+ *p ^= mask;
+ local_irq_restore(flags);
+}
+
+static inline int
+____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p)
+{
+ unsigned long flags;
+ unsigned int res;
+ unsigned long mask = 1UL << (bit & 31);
+
+ p += bit >> 5;
+
+ local_irq_save(flags);
+ res = *p;
+ *p = res | mask;
+ local_irq_restore(flags);
+
+ return res & mask;
+}
+
+static inline int
+____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p)
+{
+ unsigned long flags;
+ unsigned int res;
+ unsigned long mask = 1UL << (bit & 31);
+
+ p += bit >> 5;
+
+ local_irq_save(flags);
+ res = *p;
+ *p = res & ~mask;
+ local_irq_restore(flags);
+
+ return res & mask;
+}
+
+static inline int
+____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p)
+{
+ unsigned long flags;
+ unsigned int res;
+ unsigned long mask = 1UL << (bit & 31);
+
+ p += bit >> 5;
+
+ local_irq_save(flags);
+ res = *p;
+ *p = res ^ mask;
+ local_irq_restore(flags);
+
+ return res & mask;
+}
+
+//#include <asm-generic/bitops/non-atomic.h>
+
+/*
+ * A note about Endian-ness.
+ * -------------------------
+ *
+ * When the ARM is put into big endian mode via CR15, the processor
+ * merely swaps the order of bytes within words, thus:
+ *
+ * ------------ physical data bus bits -----------
+ * D31 ... D24 D23 ... D16 D15 ... D8 D7 ... D0
+ * little byte 3 byte 2 byte 1 byte 0
+ * big byte 0 byte 1 byte 2 byte 3
+ *
+ * This means that reading a 32-bit word at address 0 returns the same
+ * value irrespective of the endian mode bit.
+ *
+ * Peripheral devices should be connected with the data bus reversed in
+ * "Big Endian" mode. ARM Application Note 61 is applicable, and is
+ * available from http://www.arm.com/.
+ *
+ * The following assumes that the data bus connectivity for big endian
+ * mode has been followed.
+ *
+ * Note that bit 0 is defined to be 32-bit word bit 0, not byte 0 bit 0.
+ */
+
+/*
+ * Little endian assembly bitops. nr = 0 -> byte 0 bit 0.
+ */
+extern void _set_bit_le(int nr, volatile unsigned long * p);
+extern void _clear_bit_le(int nr, volatile unsigned long * p);
+extern void _change_bit_le(int nr, volatile unsigned long * p);
+extern int _test_and_set_bit_le(int nr, volatile unsigned long * p);
+extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p);
+extern int _test_and_change_bit_le(int nr, volatile unsigned long * p);
+extern int _find_first_zero_bit_le(const void * p, unsigned size);
+extern int _find_next_zero_bit_le(const void * p, int size, int offset);
+extern int _find_first_bit_le(const unsigned long *p, unsigned size);
+extern int _find_next_bit_le(const unsigned long *p, int size, int offset);
+
+/*
+ * Big endian assembly bitops. nr = 0 -> byte 3 bit 0.
+ */
+extern void _set_bit_be(int nr, volatile unsigned long * p);
+extern void _clear_bit_be(int nr, volatile unsigned long * p);
+extern void _change_bit_be(int nr, volatile unsigned long * p);
+extern int _test_and_set_bit_be(int nr, volatile unsigned long * p);
+extern int _test_and_clear_bit_be(int nr, volatile unsigned long * p);
+extern int _test_and_change_bit_be(int nr, volatile unsigned long * p);
+extern int _find_first_zero_bit_be(const void * p, unsigned size);
+extern int _find_next_zero_bit_be(const void * p, int size, int offset);
+extern int _find_first_bit_be(const unsigned long *p, unsigned size);
+extern int _find_next_bit_be(const unsigned long *p, int size, int offset);
+
+/*
+ * The __* form of bitops are non-atomic and may be reordered.
+ */
+#define ATOMIC_BITOP_LE(name,nr,p) \
+ (__builtin_constant_p(nr) ? \
+ ____atomic_##name(nr, p) : \
+ _##name##_le(nr,p))
+
+#define ATOMIC_BITOP_BE(name,nr,p) \
+ (__builtin_constant_p(nr) ? \
+ ____atomic_##name(nr, p) : \
+ _##name##_be(nr,p))
+
+#define NONATOMIC_BITOP(name,nr,p) \
+ (____nonatomic_##name(nr, p))
+
+/*
+ * These are the little endian, atomic definitions.
+ */
+#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p)
+#define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p)
+#define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p)
+#define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p)
+#define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p)
+#define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p)
+#define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz)
+#define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off)
+#define find_first_bit(p,sz) _find_first_bit_le(p,sz)
+#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off)
+
+#define WORD_BITOFF_TO_LE(x) ((x))
+
+#if 0
+#include <asm-generic/bitops/ffz.h>
+#include <asm-generic/bitops/__ffs.h>
+#include <asm-generic/bitops/fls.h>
+#include <asm-generic/bitops/ffs.h>
+
+#include <asm-generic/bitops/fls64.h>
+
+#include <asm-generic/bitops/sched.h>
+#include <asm-generic/bitops/hweight.h>
+#endif
+
+#define BITS_PER_LONG 32
+#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
+#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
+
+static inline int test_bit(int nr, const volatile unsigned long *addr)
+{
+ return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
+}
+
+#endif /* _ARM_BITOPS_H */
diff --git a/src/target/firmware/include/asm/div64.h b/src/target/firmware/include/asm/div64.h
new file mode 100644
index 00000000..36826168
--- /dev/null
+++ b/src/target/firmware/include/asm/div64.h
@@ -0,0 +1,48 @@
+#ifndef __ASM_ARM_DIV64
+#define __ASM_ARM_DIV64
+
+#include <asm/system.h>
+
+/*
+ * The semantics of do_div() are:
+ *
+ * uint32_t do_div(uint64_t *n, uint32_t base)
+ * {
+ * uint32_t remainder = *n % base;
+ * *n = *n / base;
+ * return remainder;
+ * }
+ *
+ * In other words, a 64-bit dividend with a 32-bit divisor producing
+ * a 64-bit result and a 32-bit remainder. To accomplish this optimally
+ * we call a special __do_div64 helper with completely non standard
+ * calling convention for arguments and results (beware).
+ */
+
+#ifdef __ARMEB__
+#define __xh "r0"
+#define __xl "r1"
+#else
+#define __xl "r0"
+#define __xh "r1"
+#endif
+
+#define do_div(n,base) \
+({ \
+ register unsigned int __base asm("r4") = base; \
+ register unsigned long long __n asm("r0") = n; \
+ register unsigned long long __res asm("r2"); \
+ register unsigned int __rem asm(__xh); \
+ asm( __asmeq("%0", __xh) \
+ __asmeq("%1", "r2") \
+ __asmeq("%2", "r0") \
+ __asmeq("%3", "r4") \
+ "bl __do_div64" \
+ : "=r" (__rem), "=r" (__res) \
+ : "r" (__n), "r" (__base) \
+ : "ip", "lr", "cc"); \
+ n = __res; \
+ __rem; \
+})
+
+#endif
diff --git a/src/target/firmware/include/asm/linkage.h b/src/target/firmware/include/asm/linkage.h
new file mode 100644
index 00000000..ac1c900f
--- /dev/null
+++ b/src/target/firmware/include/asm/linkage.h
@@ -0,0 +1,18 @@
+#ifndef __ASM_LINKAGE_H
+#define __ASM_LINKAGE_H
+
+/* asm-arm/linkage.h */
+
+#define __ALIGN .align 0
+#define __ALIGN_STR ".align 0"
+
+/* linux/linkage.h */
+
+#define ALIGN __ALIGN
+
+#define ENTRY(name) \
+ .globl name; \
+ ALIGN; \
+ name:
+
+#endif
diff --git a/src/target/firmware/include/asm/ptrace.h b/src/target/firmware/include/asm/ptrace.h
new file mode 100644
index 00000000..f3a654e3
--- /dev/null
+++ b/src/target/firmware/include/asm/ptrace.h
@@ -0,0 +1,128 @@
+/*
+ * linux/include/asm-arm/ptrace.h
+ *
+ * Copyright (C) 1996-2003 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ASM_ARM_PTRACE_H
+#define __ASM_ARM_PTRACE_H
+
+/*
+ * PSR bits
+ */
+#define USR26_MODE 0x00000000
+#define FIQ26_MODE 0x00000001
+#define IRQ26_MODE 0x00000002
+#define SVC26_MODE 0x00000003
+#define USR_MODE 0x00000010
+#define FIQ_MODE 0x00000011
+#define IRQ_MODE 0x00000012
+#define SVC_MODE 0x00000013
+#define ABT_MODE 0x00000017
+#define UND_MODE 0x0000001b
+#define SYSTEM_MODE 0x0000001f
+#define MODE32_BIT 0x00000010
+#define MODE_MASK 0x0000001f
+#define PSR_T_BIT 0x00000020
+#define PSR_F_BIT 0x00000040
+#define PSR_I_BIT 0x00000080
+#define PSR_J_BIT 0x01000000
+#define PSR_Q_BIT 0x08000000
+#define PSR_V_BIT 0x10000000
+#define PSR_C_BIT 0x20000000
+#define PSR_Z_BIT 0x40000000
+#define PSR_N_BIT 0x80000000
+#define PCMASK 0
+
+/*
+ * Groups of PSR bits
+ */
+#define PSR_f 0xff000000 /* Flags */
+#define PSR_s 0x00ff0000 /* Status */
+#define PSR_x 0x0000ff00 /* Extension */
+#define PSR_c 0x000000ff /* Control */
+
+#ifndef __ASSEMBLY__
+
+/*
+ * This struct defines the way the registers are stored on the
+ * stack during a system call. Note that sizeof(struct pt_regs)
+ * has to be a multiple of 8.
+ */
+struct pt_regs {
+ long uregs[18];
+};
+
+#define ARM_cpsr uregs[16]
+#define ARM_pc uregs[15]
+#define ARM_lr uregs[14]
+#define ARM_sp uregs[13]
+#define ARM_ip uregs[12]
+#define ARM_fp uregs[11]
+#define ARM_r10 uregs[10]
+#define ARM_r9 uregs[9]
+#define ARM_r8 uregs[8]
+#define ARM_r7 uregs[7]
+#define ARM_r6 uregs[6]
+#define ARM_r5 uregs[5]
+#define ARM_r4 uregs[4]
+#define ARM_r3 uregs[3]
+#define ARM_r2 uregs[2]
+#define ARM_r1 uregs[1]
+#define ARM_r0 uregs[0]
+#define ARM_ORIG_r0 uregs[17]
+
+#define user_mode(regs) \
+ (((regs)->ARM_cpsr & 0xf) == 0)
+
+#ifdef CONFIG_ARM_THUMB
+#define thumb_mode(regs) \
+ (((regs)->ARM_cpsr & PSR_T_BIT))
+#else
+#define thumb_mode(regs) (0)
+#endif
+
+#define processor_mode(regs) \
+ ((regs)->ARM_cpsr & MODE_MASK)
+
+#define interrupts_enabled(regs) \
+ (!((regs)->ARM_cpsr & PSR_I_BIT))
+
+#define fast_interrupts_enabled(regs) \
+ (!((regs)->ARM_cpsr & PSR_F_BIT))
+
+#define condition_codes(regs) \
+ ((regs)->ARM_cpsr & (PSR_V_BIT|PSR_C_BIT|PSR_Z_BIT|PSR_N_BIT))
+
+/* Are the current registers suitable for user mode?
+ * (used to maintain security in signal handlers)
+ */
+static inline int valid_user_regs(struct pt_regs *regs)
+{
+ if (user_mode(regs) &&
+ (regs->ARM_cpsr & (PSR_F_BIT|PSR_I_BIT)) == 0)
+ return 1;
+
+ /*
+ * Force CPSR to something logical...
+ */
+ regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | PSR_T_BIT | MODE32_BIT;
+
+ return 0;
+}
+
+#define pc_pointer(v) \
+ ((v) & ~PCMASK)
+
+#define instruction_pointer(regs) \
+ (pc_pointer((regs)->ARM_pc))
+
+#define profile_pc(regs) instruction_pointer(regs)
+
+#endif /* __ASSEMBLY__ */
+
+#endif
+
diff --git a/src/target/firmware/include/asm/swab.h b/src/target/firmware/include/asm/swab.h
new file mode 100644
index 00000000..4640e271
--- /dev/null
+++ b/src/target/firmware/include/asm/swab.h
@@ -0,0 +1,45 @@
+/*
+ * arch/arm/include/asm/byteorder.h
+ *
+ * ARM Endian-ness. In little endian mode, the data bus is connected such
+ * that byte accesses appear as:
+ * 0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31
+ * and word accesses (data or instruction) appear as:
+ * d0...d31
+ *
+ * When in big endian mode, byte accesses appear as:
+ * 0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7
+ * and word accesses (data or instruction) appear as:
+ * d0...d31
+ */
+#ifndef __ASM_ARM_SWAB_H
+#define __ASM_ARM_SWAB_H
+
+#include <stdint.h>
+#include <defines.h>
+
+static inline uint32_t __arch_swab32(uint32_t x)
+{
+ uint32_t t;
+
+#ifndef __thumb__
+ if (!__builtin_constant_p(x)) {
+ /*
+ * The compiler needs a bit of a hint here to always do the
+ * right thing and not screw it up to different degrees
+ * depending on the gcc version.
+ */
+ asm ("eor\t%0, %1, %1, ror #16" : "=r" (t) : "r" (x));
+ } else
+#endif
+ t = x ^ ((x << 16) | (x >> 16)); /* eor r1,r0,r0,ror #16 */
+
+ x = (x << 24) | (x >> 8); /* mov r0,r0,ror #8 */
+ t &= ~0x00FF0000; /* bic r1,r1,#0x00FF0000 */
+ x ^= (t >> 8); /* eor r0,r0,r1,lsr #8 */
+
+ return x;
+}
+#define __arch_swab32 __arch_swab32
+
+#endif
diff --git a/src/target/firmware/include/asm/system.h b/src/target/firmware/include/asm/system.h
new file mode 100644
index 00000000..3db0dc7a
--- /dev/null
+++ b/src/target/firmware/include/asm/system.h
@@ -0,0 +1,123 @@
+#ifndef __ASM_ARM_SYSTEM_H
+#define __ASM_ARM_SYSTEM_H
+
+/* Generic ARM7TDMI (ARMv4T) synchronisation primitives, mostly
+ * taken from Linux kernel source, licensed under GPL */
+
+#define local_irq_save(x) \
+ ({ \
+ unsigned long temp; \
+ (void) (&temp == &x); \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ local_irq_save\n" \
+" orr %1, %0, #128\n" \
+" msr cpsr_c, %1" \
+ : "=r" (x), "=r" (temp) \
+ : \
+ : "memory", "cc"); \
+ })
+
+/* Save IRQ flags and disable FIQ + IRQ */
+#define local_firq_save(x) \
+ ({ \
+ unsigned long temp; \
+ (void) (&temp == &x); \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ local_firq_save\n" \
+" orr %1, %0, #0xC0\n" \
+" msr cpsr_c, %1" \
+ : "=r" (x), "=r" (temp) \
+ : \
+ : "memory", "cc"); \
+ })
+
+/*
+ * Enable IRQs
+ */
+#define local_irq_enable() \
+ ({ \
+ unsigned long temp; \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ local_irq_enable\n" \
+" bic %0, %0, #128\n" \
+" msr cpsr_c, %0" \
+ : "=r" (temp) \
+ : \
+ : "memory", "cc"); \
+ })
+
+/*
+ * Disable IRQs
+ */
+#define local_irq_disable() \
+ ({ \
+ unsigned long temp; \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ local_irq_disable\n" \
+" orr %0, %0, #128\n" \
+" msr cpsr_c, %0" \
+ : "=r" (temp) \
+ : \
+ : "memory", "cc"); \
+ })
+
+/*
+ * Enable FIQs
+ */
+#define local_fiq_enable() \
+ ({ \
+ unsigned long temp; \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ stf\n" \
+" bic %0, %0, #64\n" \
+" msr cpsr_c, %0" \
+ : "=r" (temp) \
+ : \
+ : "memory", "cc"); \
+ })
+
+/*
+ * Disable FIQs
+ */
+#define local_fiq_disable() \
+ ({ \
+ unsigned long temp; \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ clf\n" \
+" orr %0, %0, #64\n" \
+" msr cpsr_c, %0" \
+ : "=r" (temp) \
+ : \
+ : "memory", "cc"); \
+ })
+
+/*
+ * Save the current interrupt enable state.
+ */
+#define local_save_flags(x) \
+ ({ \
+ __asm__ __volatile__( \
+ "mrs %0, cpsr @ local_save_flags" \
+ : "=r" (x) : : "memory", "cc"); \
+ })
+
+/*
+ * restore saved IRQ & FIQ state
+ */
+#define local_irq_restore(x) \
+ __asm__ __volatile__( \
+ "msr cpsr_c, %0 @ local_irq_restore\n" \
+ : \
+ : "r" (x) \
+ : "memory", "cc")
+
+#define irqs_disabled() \
+({ \
+ unsigned long flags; \
+ local_save_flags(flags); \
+ (int)(flags & PSR_I_BIT); \
+})
+
+#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t"
+
+#endif
diff --git a/src/target/firmware/include/board.h b/src/target/firmware/include/board.h
new file mode 100644
index 00000000..c2fb6018
--- /dev/null
+++ b/src/target/firmware/include/board.h
@@ -0,0 +1,6 @@
+#ifndef _BOARD_H
+#define _BOARD_H
+
+extern const char *target_board;
+
+#endif /* _BOARD_H */
diff --git a/src/target/firmware/include/byteorder.h b/src/target/firmware/include/byteorder.h
new file mode 100644
index 00000000..41edb93d
--- /dev/null
+++ b/src/target/firmware/include/byteorder.h
@@ -0,0 +1,79 @@
+#ifndef _LINUX_BYTEORDER_LITTLE_ENDIAN_H
+#define _LINUX_BYTEORDER_LITTLE_ENDIAN_H
+
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#endif
+#ifndef __LITTLE_ENDIAN_BITFIELD
+#define __LITTLE_ENDIAN_BITFIELD
+#endif
+
+#include <stdint.h>
+#include <swab.h>
+
+#define __constant_htonl(x) ___constant_swab32(x)
+#define __constant_ntohl(x) ___constant_swab32(x)
+#define __constant_htons(x) ___constant_swab16(x)
+#define __constant_ntohs(x) ___constant_swab16(x)
+#define __constant_cpu_to_le64(x) (x)
+#define __constant_le64_to_cpu(x) (x)
+#define __constant_cpu_to_le32(x) (x)
+#define __constant_le32_to_cpu(x) (x)
+#define __constant_cpu_to_le16(x) (x)
+#define __constant_le16_to_cpu(x) (x)
+#define __constant_cpu_to_be64(x) ___constant_swab64(x)
+#define __constant_be64_to_cpu(x) ___constant_swab64(x)
+#define __constant_cpu_to_be32(x) ___constant_swab32(x)
+#define __constant_be32_to_cpu(x) ___constant_swab32(x)
+#define __constant_cpu_to_be16(x) ___constant_swab16(x)
+#define __constant_be16_to_cpu(x) ___constant_swab16(x)
+#define __cpu_to_le64(x) (x)
+#define __le64_to_cpu(x) (x)
+#define __cpu_to_le32(x) (x)
+#define __le32_to_cpu(x) (x)
+#define __cpu_to_le16(x) (x)
+#define __le16_to_cpu(x) (x)
+#define __cpu_to_be64(x) __swab64(x)
+#define __be64_to_cpu(x) __swab64(x)
+#define __cpu_to_be32(x) __swab32(x)
+#define __be32_to_cpu(x) __swab32(x)
+#define __cpu_to_be16(x) __swab16(x)
+#define __be16_to_cpu(x) __swab16(x)
+
+/* from include/linux/byteorder/generic.h */
+#define cpu_to_le64 __cpu_to_le64
+#define le64_to_cpu __le64_to_cpu
+#define cpu_to_le32 __cpu_to_le32
+#define le32_to_cpu __le32_to_cpu
+#define cpu_to_le16 __cpu_to_le16
+#define le16_to_cpu __le16_to_cpu
+#define cpu_to_be64 __cpu_to_be64
+#define be64_to_cpu __be64_to_cpu
+#define cpu_to_be32 __cpu_to_be32
+#define be32_to_cpu __be32_to_cpu
+#define cpu_to_be16 __cpu_to_be16
+#define be16_to_cpu __be16_to_cpu
+
+/*
+ * They have to be macros in order to do the constant folding
+ * correctly - if the argument passed into a inline function
+ * it is no longer constant according to gcc..
+ */
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+#define ___htonl(x) __cpu_to_be32(x)
+#define ___htons(x) __cpu_to_be16(x)
+#define ___ntohl(x) __be32_to_cpu(x)
+#define ___ntohs(x) __be16_to_cpu(x)
+
+#define htonl(x) ___htonl(x)
+#define ntohl(x) ___ntohl(x)
+#define htons(x) ___htons(x)
+#define ntohs(x) ___ntohs(x)
+
+
+#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */
diff --git a/src/target/firmware/include/calypso/backlight.h b/src/target/firmware/include/calypso/backlight.h
new file mode 100644
index 00000000..3a6abd55
--- /dev/null
+++ b/src/target/firmware/include/calypso/backlight.h
@@ -0,0 +1,10 @@
+#ifndef _CAL_BACKLIGHT_H
+#define _CAL_BACKLIGHT_H
+
+/* Switch backlight to PWL mode (or back) */
+void bl_mode_pwl(int on);
+
+/* Set the backlight level */
+void bl_level(uint8_t level);
+
+#endif /* CAL_BACKLIGHT_H */
diff --git a/src/target/firmware/include/calypso/clock.h b/src/target/firmware/include/calypso/clock.h
new file mode 100644
index 00000000..abcfde1d
--- /dev/null
+++ b/src/target/firmware/include/calypso/clock.h
@@ -0,0 +1,67 @@
+#ifndef _CALYPSO_CLK_H
+#define _CALYPSO_CLK_H
+
+#include <stdint.h>
+
+#define CALYPSO_PLL26_52_MHZ ((2 << 8) | 0)
+#define CALYPSO_PLL26_86_7_MHZ ((10 << 8) | 2)
+#define CALYPSO_PLL26_87_MHZ ((3 << 8) | 0)
+#define CALYPSO_PLL13_104_MHZ ((8 << 8) | 0)
+
+enum mclk_div {
+ _ARM_MCLK_DIV_1 = 0,
+ ARM_MCLK_DIV_1 = 1,
+ ARM_MCLK_DIV_2 = 2,
+ ARM_MCLK_DIV_3 = 3,
+ ARM_MCLK_DIV_4 = 4,
+ ARM_MCLK_DIV_5 = 5,
+ ARM_MCLK_DIV_6 = 6,
+ ARM_MCLK_DIV_7 = 7,
+ ARM_MCLK_DIV_1_5 = 0x80 | 1,
+ ARM_MCLK_DIV_2_5 = 0x80 | 2,
+};
+
+void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div);
+void calypso_pll_set(uint16_t inp);
+void calypso_clk_dump(void);
+
+/* CNTL_RST */
+enum calypso_rst {
+ RESET_DSP = (1 << 1),
+ RESET_EXT = (1 << 2),
+ RESET_WDOG = (1 << 3),
+};
+
+void calypso_reset_set(enum calypso_rst calypso_rst, int active);
+int calypso_reset_get(enum calypso_rst);
+
+enum calypso_bank {
+ CALYPSO_nCS0 = 0,
+ CALYPSO_nCS1 = 2,
+ CALYPSO_nCS2 = 4,
+ CALYPSO_nCS3 = 6,
+ CALYPSO_nCS7 = 8,
+ CALYPSO_CS4 = 0xa,
+ CALYPSO_nCS6 = 0xc,
+};
+
+enum calypso_mem_width {
+ CALYPSO_MEM_8bit = 0,
+ CALYPSO_MEM_16bit = 1,
+ CALYPSO_MEM_32bit = 2,
+};
+
+void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws,
+ enum calypso_mem_width width, int we);
+
+/* Enable or disable the internal bootrom mapped to 0x0000'0000 */
+void calypso_bootrom(int enable);
+
+/* Enable or disable the debug unit */
+void calypso_debugunit(int enable);
+
+/* configure the RHEA bus bridge[s] */
+void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout,
+ uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1);
+
+#endif /* _CALYPSO_CLK_H */
diff --git a/src/target/firmware/include/calypso/dma.h b/src/target/firmware/include/calypso/dma.h
new file mode 100644
index 00000000..00b9bde7
--- /dev/null
+++ b/src/target/firmware/include/calypso/dma.h
@@ -0,0 +1,6 @@
+#ifndef _CALYPSO_DMA_H
+#define _CALYPSO_DMA_H
+
+void dma_init(void);
+
+#endif /* _CALYPSO_DMA_H */
diff --git a/src/target/firmware/include/calypso/dsp.h b/src/target/firmware/include/calypso/dsp.h
new file mode 100644
index 00000000..4839f827
--- /dev/null
+++ b/src/target/firmware/include/calypso/dsp.h
@@ -0,0 +1,35 @@
+#ifndef _CALYPSO_DSP_H
+#define _CALYPSO_DSP_H
+
+#include <calypso/dsp_api.h>
+
+#define CAL_DSP_TGT_BB_LVL 80
+
+struct dsp_api {
+ T_NDB_MCU_DSP *ndb;
+ T_DB_DSP_TO_MCU *db_r;
+ T_DB_MCU_TO_DSP *db_w;
+ T_PARAM_MCU_DSP *param;
+ int r_page;
+ int w_page;
+ int r_page_used;
+ int frame_ctr;
+};
+
+extern struct dsp_api dsp_api;
+
+void dsp_power_on(void);
+void dsp_dump_version(void);
+void dsp_dump(void);
+void dsp_checksum_task(void);
+void dsp_api_memset(uint16_t *ptr, int octets);
+void dsp_load_afc_dac(uint16_t afc);
+void dsp_load_apc_dac(uint16_t apc);
+void dsp_load_tch_param(uint16_t fn, uint8_t chan_mode, uint8_t chan_type,
+ uint8_t subchannel, uint8_t tch_loop, uint8_t sync_tch);
+void dsp_end_scenario(void);
+
+void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc);
+void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc);
+
+#endif
diff --git a/src/target/firmware/include/calypso/dsp_api.h b/src/target/firmware/include/calypso/dsp_api.h
new file mode 100644
index 00000000..f9751f37
--- /dev/null
+++ b/src/target/firmware/include/calypso/dsp_api.h
@@ -0,0 +1,1560 @@
+#ifndef _CAL_DSP_API_H
+#define _CAL_DSP_API_H
+
+/* This is a header file with structures imported from the TSM30 source code (l1_defty.h)
+ *
+ * As this header file only is a list of definitions and data structures, it is
+ * not ocnsidered to be a copyrightable work itself.
+ *
+ * Nonetheless, it might be good to rewrite it (without ugly typedefs!) */
+
+#if(L1_DYN_DSP_DWNLD == 1)
+ #include "l1_dyn_dwl_defty.h"
+#endif
+
+/* Include a header file that defines everything this l1_defty.h needs */
+#include "l1_environment.h"
+
+#define BASE_API_NDB 0xFFD001A8L /* 268 words */
+#define BASE_API_PARAM 0xFFD00862L /* 57 words */
+#define BASE_API_R_PAGE_0 0xFFD00050L /* 20 words */
+#define BASE_API_R_PAGE_1 0xFFD00078L /* 20 words */
+#define BASE_API_W_PAGE_0 0xFFD00000L /* 20 words */
+#define BASE_API_W_PAGE_1 0xFFD00028L /* 20 words */
+
+
+/***********************************************************/
+/* */
+/* Data structure for global info components. */
+/* */
+/***********************************************************/
+
+typedef struct
+{
+ API d_task_d; // (0) Downlink task command.
+ API d_burst_d; // (1) Downlink burst identifier.
+ API d_task_u; // (2) Uplink task command.
+ API d_burst_u; // (3) Uplink burst identifier.
+ API d_task_md; // (4) Downlink Monitoring (FB/SB) command.
+#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36)
+ API d_background; // (5) Background tasks
+#else
+ API d_reserved; // (5) Reserved
+#endif
+ API d_debug; // (6) Debug/Acknowledge/general purpose word.
+ API d_task_ra; // (7) RA task command.
+ API d_fn; // (8) FN, in Rep. period and FN%104, used for TRAFFIC/TCH only.
+ // bit [0..7] -> b_fn_report, FN in the normalized reporting period.
+ // bit [8..15] -> b_fn_sid, FN % 104, used for SID positionning.
+ API d_ctrl_tch; // (9) Tch channel description.
+ // bit [0..3] -> b_chan_mode, channel mode.
+ // bit [4..5] -> b_chan_type, channel type.
+ // bit [6] -> reset SACCH
+ // bit [7] -> vocoder ON
+ // bit [8] -> b_sync_tch_ul, synchro. TCH/UL.
+ // bit [9] -> b_sync_tch_dl, synchro. TCH/DL.
+ // bit [10] -> b_stop_tch_ul, stop TCH/UL.
+ // bit [11] -> b_stop_tch_dl, stop TCH/DL.
+ // bit [12.13] -> b_tch_loop, tch loops A/B/C.
+ API hole; // (10) unused hole.
+
+#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3))
+ API d_ctrl_abb; // (11) Bit field indicating the analog baseband register to send.
+ // bit [0] -> b_ramp: the ramp information(a_ramp[]) is located in NDB
+ // bit [1.2] -> unused
+ // bit [3] -> b_apcdel: delays-register in NDB
+ // bit [4] -> b_afc: freq control register in DB
+ // bit [5..15] -> unused
+#endif
+ API a_a5fn[2]; // (12..13) Encryption Frame number.
+ // word 0, bit [0..4] -> T2.
+ // word 0, bit [5..10] -> T3.
+ // word 1, bit [0..11] -> T1.
+ API d_power_ctl; // (14) Power level control.
+ API d_afc; // (15) AFC value (enabled by "b_afc" in "d_ctrl_TCM4400 or in d_ctrl_abb").
+ API d_ctrl_system; // (16) Controle Register for RESET/RESUME.
+ // bit [0..2] -> b_tsq, training sequence.
+ // bit [3] -> b_bcch_freq_ind, BCCH frequency indication.
+ // bit [15] -> b_task_abort, DSP task abort command.
+}
+T_DB_MCU_TO_DSP;
+
+typedef struct
+{
+ API d_task_d; // (0) Downlink task command.
+ API d_burst_d; // (1) Downlink burst identifier.
+ API d_task_u; // (2) Uplink task command.
+ API d_burst_u; // (3) Uplink burst identifier.
+ API d_task_md; // (4) Downlink Monitoring (FB/SB) task command.
+#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36)
+ API d_background; // (5) Background tasks
+#else
+ API d_reserved; // (5) Reserved
+#endif
+ API d_debug; // (6) Debug/Acknowledge/general purpose word.
+ API d_task_ra; // (7) RA task command.
+
+#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36)
+ API a_serv_demod[4]; // ( 8..11) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR).
+ API a_pm[3]; // (12..14) Power measurement results, array of 3 words.
+ API a_sch[5]; // (15..19) Header + SB information, array of 5 words.
+#else
+ API a_pm[3]; // ( 8..10) Power measurement results, array of 3 words.
+ API a_serv_demod[4]; // (11..14) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR).
+ API a_sch[5]; // (15..19) Header + SB information, array of 5 words.
+#endif
+}
+T_DB_DSP_TO_MCU;
+
+#if (DSP == 34) || (DSP == 35) || (DSP == 36) // NDB GSM
+ typedef struct
+ {
+ // MISC Tasks
+ API d_dsp_page;
+
+ // DSP status returned (DSP --> MCU).
+ API d_error_status;
+
+ // RIF control (MCU -> DSP).
+ API d_spcx_rif;
+
+ API d_tch_mode; // TCH mode register.
+ // bit [0..1] -> b_dai_mode.
+ // bit [2] -> b_dtx.
+
+ API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega
+
+ API d_dsp_test;
+
+ // Words dedicated to Software version (DSP code + Patch)
+ API d_version_number1;
+ API d_version_number2;
+
+ API d_debug_ptr;
+ API d_debug_bk;
+
+ API d_pll_config;
+
+ // GSM/GPRS DSP Debug trace support
+ API p_debug_buffer;
+ API d_debug_buffer_size;
+ API d_debug_trace_type;
+
+ #if (W_A_DSP_IDLE3 == 1)
+ // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3.
+ API d_dsp_state;
+ // 5 words are reserved for any possible mapping modification
+ API d_hole1_ndb[2];
+ #else
+ // 6 words are reserved for any possible mapping modification
+ API d_hole1_ndb[3];
+ #endif
+
+ #if (AMR == 1)
+ API p_debug_amr;
+ #else
+ API d_hole_debug_amr;
+ #endif
+
+ #if (CHIPSET == 12)
+ #if (DSP == 35) || (DSP == 36)
+ API d_hole2_ndb[1];
+ API d_mcsi_select;
+ #else
+ API d_hole2_ndb[2];
+ #endif
+ #else
+ API d_hole2_ndb[2];
+ #endif
+
+ // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations
+ API d_apcdel1_bis;
+ API d_apcdel2_bis;
+
+
+ // New registers due to IOTA analog base band
+ API d_apcdel2;
+ API d_vbctrl2;
+ API d_bulgcal;
+
+ // Analog Based Band
+ API d_afcctladd;
+
+ API d_vbuctrl;
+ API d_vbdctrl;
+ API d_apcdel1;
+ API d_apcoff;
+ API d_bulioff;
+ API d_bulqoff;
+ API d_dai_onoff;
+ API d_auxdac;
+
+ #if (ANLG_FAM == 1)
+ API d_vbctrl;
+ #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3))
+ API d_vbctrl1;
+ #endif
+
+ API d_bbctrl;
+
+ // Monitoring tasks control (MCU <- DSP)
+ // FB task
+ API d_fb_det; // FB detection result. (1 for FOUND).
+ API d_fb_mode; // Mode for FB detection algorithm.
+ API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR).
+
+ // SB Task
+ API a_sch26[5]; // Header + SB information, array of 5 words.
+
+ API d_audio_gain_ul;
+ API d_audio_gain_dl;
+
+ // Controller of the melody E2 audio compressor
+ API d_audio_compressor_ctrl;
+
+ // AUDIO module
+ API d_audio_init;
+ API d_audio_status;
+
+ // Audio tasks
+ // TONES (MCU -> DSP)
+ API d_toneskb_init;
+ API d_toneskb_status;
+ API d_k_x1_t0;
+ API d_k_x1_t1;
+ API d_k_x1_t2;
+ API d_pe_rep;
+ API d_pe_off;
+ API d_se_off;
+ API d_bu_off;
+ API d_t0_on;
+ API d_t0_off;
+ API d_t1_on;
+ API d_t1_off;
+ API d_t2_on;
+ API d_t2_off;
+ API d_k_x1_kt0;
+ API d_k_x1_kt1;
+ API d_dur_kb;
+ API d_shiftdl;
+ API d_shiftul;
+
+ API d_aec_ctrl;
+
+ API d_es_level_api;
+ API d_mu_api;
+
+ // Melody Ringer module
+ API d_melo_osc_used;
+ API d_melo_osc_active;
+ API a_melo_note0[4];
+ API a_melo_note1[4];
+ API a_melo_note2[4];
+ API a_melo_note3[4];
+ API a_melo_note4[4];
+ API a_melo_note5[4];
+ API a_melo_note6[4];
+ API a_melo_note7[4];
+
+ // selection of the melody format
+ API d_melody_selection;
+
+ // Holes due to the format melody E1
+ API a_melo_holes[3];
+
+ // Speech Recognition module
+ API d_sr_status; // status of the DSP speech reco task
+ API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API d_sr_bit_exact_test; // bit exact test
+ API d_sr_nb_words; // number of words used in the speech recognition task
+ API d_sr_db_level; // estimate voice level in dB
+ API d_sr_db_noise; // estimate noise in dB
+ API d_sr_mod_size; // size of the model
+ API a_n_best_words[4]; // array of the 4 best words
+ API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length)
+
+ // Audio buffer
+ API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1.
+ API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1.
+
+ // V42bis module
+ API d_v42b_nego0;
+ API d_v42b_nego1;
+ API d_v42b_control;
+ API d_v42b_ratio_ind;
+ API d_mcu_control;
+ API d_mcu_control_sema;
+
+ // Background tasks
+ API d_background_enable;
+ API d_background_abort;
+ API d_background_state;
+ API d_max_background;
+ API a_background_tasks[16];
+ API a_back_task_io[16];
+
+ // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory)
+ API d_gea_mode_ovly;
+ API a_gea_kc_ovly[4];
+
+#if (ANLG_FAM == 3)
+ // SYREN specific registers
+ API d_vbpop;
+ API d_vau_delay_init;
+ API d_vaud_cfg;
+ API d_vauo_onoff;
+ API d_vaus_vol;
+ API d_vaud_pll;
+ API d_hole3_ndb[1];
+#elif ((ANLG_FAM == 1) || (ANLG_FAM == 2))
+
+ API d_hole3_ndb[7];
+
+#endif
+
+ // word used for the init of USF threshold
+ API d_thr_usf_detect;
+
+ // Encryption module
+ API d_a5mode; // Encryption Mode.
+
+ API d_sched_mode_gprs_ovly;
+
+ // 7 words are reserved for any possible mapping modification
+ API d_hole4_ndb[5];
+
+ // Ramp definition for Omega device
+ API a_ramp[16];
+
+ // CCCH/SACCH downlink information...(!!)
+ API a_cd[15]; // Header + CCCH/SACCH downlink information.
+
+ // FACCH downlink information........(!!)
+ API a_fd[15]; // Header + FACCH downlink information.
+
+ // Traffic downlink data frames......(!!)
+ API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0.
+
+ // CCCH/SACCH uplink information.....(!!)
+ API a_cu[15]; // Header + CCCH/SACCH uplink information.
+
+ // FACCH downlink information........(!!)
+ API a_fu[15]; // Header + FACCH uplink information
+
+ // Traffic downlink data frames......(!!)
+ API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0.
+
+ // Random access.....................(MCU -> DSP).
+ API d_rach; // RACH information.
+
+ //...................................(MCU -> DSP).
+ API a_kc[4]; // Encryption Key Code.
+
+ // Integrated Data Services module
+ API d_ra_conf;
+ API d_ra_act;
+ API d_ra_test;
+ API d_ra_statu;
+ API d_ra_statd;
+ API d_fax;
+ API a_data_buf_ul[21];
+ API a_data_buf_dl[37];
+
+ // GTT API mapping for DSP code 34 (for test only)
+ #if (L1_GTT == 1)
+ API d_tty_status;
+ API d_tty_detect_thres;
+ API d_ctm_detect_shift;
+ API d_tty_fa_thres;
+ API d_tty_mod_norm;
+ API d_tty_reset_buffer_ul;
+ API d_tty_loop_ctrl;
+ API p_tty_loop_buffer;
+ #else
+ API a_tty_holes[8];
+ #endif
+
+ API a_sr_holes0[414];
+
+ #if (L1_NEW_AEC)
+ // new AEC
+ API d_cont_filter;
+ API d_granularity_att;
+ API d_coef_smooth;
+ API d_es_level_max;
+ API d_fact_vad;
+ API d_thrs_abs;
+ API d_fact_asd_fil;
+ API d_fact_asd_mut;
+ API d_far_end_pow_h;
+ API d_far_end_pow_l;
+ API d_far_end_noise_h;
+ API d_far_end_noise_l;
+ #else
+ API a_new_aec_holes[12];
+ #endif // L1_NEW_AEC
+
+ // Speech recognition model
+ API a_sr_holes1[145];
+ API d_cport_init;
+ API d_cport_ctrl;
+ API a_cport_cfr[2];
+ API d_cport_tcl_tadt;
+ API d_cport_tdat;
+ API d_cport_tvs;
+ API d_cport_status;
+ API d_cport_reg_value;
+
+ API a_cport_holes[1011];
+
+ API a_model[1041];
+
+ // EOTD buffer
+#if (L1_EOTD==1)
+ API d_eotd_first;
+ API d_eotd_max;
+ API d_eotd_nrj_high;
+ API d_eotd_nrj_low;
+ API a_eotd_crosscor[18];
+#else
+ API a_eotd_holes[22];
+#endif
+ // AMR ver 1.0 buffers
+ API a_amr_config[4];
+ API a_ratscch_ul[6];
+ API a_ratscch_dl[6];
+ API d_amr_snr_est; // estimation of the SNR of the AMR speech block
+ #if (L1_VOICE_MEMO_AMR)
+ API d_amms_ul_voc;
+ #else
+ API a_voice_memo_amr_holes[1];
+ #endif
+ API d_thr_onset_afs; // thresh detection ONSET AFS
+ API d_thr_sid_first_afs; // thresh detection SID_FIRST AFS
+ API d_thr_ratscch_afs; // thresh detection RATSCCH AFS
+ API d_thr_update_afs; // thresh detection SID_UPDATE AFS
+ API d_thr_onset_ahs; // thresh detection ONSET AHS
+ API d_thr_sid_ahs; // thresh detection SID frames AHS
+ API d_thr_ratscch_marker;// thresh detection RATSCCH MARKER
+ API d_thr_sp_dgr; // thresh detection SPEECH DEGRADED/NO_DATA
+ API d_thr_soft_bits;
+ #if (MELODY_E2)
+ API d_melody_e2_osc_stop;
+ API d_melody_e2_osc_active;
+ API d_melody_e2_semaphore;
+ API a_melody_e2_osc[16][3];
+ API d_melody_e2_globaltimefactor;
+ API a_melody_e2_instrument_ptr[8];
+ API d_melody_e2_deltatime;
+
+ #if (AMR_THRESHOLDS_WORKAROUND)
+ API a_d_macc_thr_afs[8];
+ API a_d_macc_thr_ahs[6];
+ #else
+ API a_melody_e2_holes0[14];
+ #endif
+
+ API a_melody_e2_holes1[693];
+ API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE];
+ API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT];
+ #else
+ API d_holes[61];
+ #if (AMR_THRESHOLDS_WORKAROUND)
+ API a_d_macc_thr_afs[8];
+ API a_d_macc_thr_ahs[6];
+ #endif
+ #endif
+
+ }
+ T_NDB_MCU_DSP;
+#elif (DSP == 33) // NDB GSM
+ typedef struct
+ {
+ // MISC Tasks
+ API d_dsp_page;
+
+ // DSP status returned (DSP --> MCU).
+ API d_error_status;
+
+ // RIF control (MCU -> DSP).
+ API d_spcx_rif;
+
+ API d_tch_mode; // TCH mode register.
+ // bit [0..1] -> b_dai_mode.
+ // bit [2] -> b_dtx.
+
+ API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega
+
+ API d_dsp_test;
+
+ // Words dedicated to Software version (DSP code + Patch)
+ API d_version_number1;
+ API d_version_number2;
+
+ API d_debug_ptr;
+ API d_debug_bk;
+
+ API d_pll_config;
+
+ // GSM/GPRS DSP Debug trace support
+ API p_debug_buffer;
+ API d_debug_buffer_size;
+ API d_debug_trace_type;
+
+ #if (W_A_DSP_IDLE3 == 1)
+ // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3.
+ API d_dsp_state;
+ // 10 words are reserved for any possible mapping modification
+ API d_hole1_ndb[5];
+ #else
+ // 11 words are reserved for any possible mapping modification
+ API d_hole1_ndb[6];
+ #endif
+
+ // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations
+ API d_apcdel1_bis;
+ API d_apcdel2_bis;
+
+
+ // New registers due to IOTA analog base band
+ API d_apcdel2;
+ API d_vbctrl2;
+ API d_bulgcal;
+
+ // Analog Based Band
+ API d_afcctladd;
+
+ API d_vbuctrl;
+ API d_vbdctrl;
+ API d_apcdel1;
+ API d_apcoff;
+ API d_bulioff;
+ API d_bulqoff;
+ API d_dai_onoff;
+ API d_auxdac;
+
+ #if (ANLG_FAM == 1)
+ API d_vbctrl;
+ #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3))
+ API d_vbctrl1;
+ #endif
+
+ API d_bbctrl;
+
+ // Monitoring tasks control (MCU <- DSP)
+ // FB task
+ API d_fb_det; // FB detection result. (1 for FOUND).
+ API d_fb_mode; // Mode for FB detection algorithm.
+ API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR).
+
+ // SB Task
+ API a_sch26[5]; // Header + SB information, array of 5 words.
+
+ API d_audio_gain_ul;
+ API d_audio_gain_dl;
+
+ // Controller of the melody E2 audio compressor
+ API d_audio_compressor_ctrl;
+
+ // AUDIO module
+ API d_audio_init;
+ API d_audio_status;
+
+ // Audio tasks
+ // TONES (MCU -> DSP)
+ API d_toneskb_init;
+ API d_toneskb_status;
+ API d_k_x1_t0;
+ API d_k_x1_t1;
+ API d_k_x1_t2;
+ API d_pe_rep;
+ API d_pe_off;
+ API d_se_off;
+ API d_bu_off;
+ API d_t0_on;
+ API d_t0_off;
+ API d_t1_on;
+ API d_t1_off;
+ API d_t2_on;
+ API d_t2_off;
+ API d_k_x1_kt0;
+ API d_k_x1_kt1;
+ API d_dur_kb;
+ API d_shiftdl;
+ API d_shiftul;
+
+ API d_aec_ctrl;
+
+ API d_es_level_api;
+ API d_mu_api;
+
+ // Melody Ringer module
+ API d_melo_osc_used;
+ API d_melo_osc_active;
+ API a_melo_note0[4];
+ API a_melo_note1[4];
+ API a_melo_note2[4];
+ API a_melo_note3[4];
+ API a_melo_note4[4];
+ API a_melo_note5[4];
+ API a_melo_note6[4];
+ API a_melo_note7[4];
+
+ // selection of the melody format
+ API d_melody_selection;
+
+ // Holes due to the format melody E1
+ API a_melo_holes[3];
+
+ // Speech Recognition module
+ API d_sr_status; // status of the DSP speech reco task
+ API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API d_sr_bit_exact_test; // bit exact test
+ API d_sr_nb_words; // number of words used in the speech recognition task
+ API d_sr_db_level; // estimate voice level in dB
+ API d_sr_db_noise; // estimate noise in dB
+ API d_sr_mod_size; // size of the model
+ API a_n_best_words[4]; // array of the 4 best words
+ API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length)
+
+ // Audio buffer
+ API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1.
+ API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1.
+
+ // V42bis module
+ API d_v42b_nego0;
+ API d_v42b_nego1;
+ API d_v42b_control;
+ API d_v42b_ratio_ind;
+ API d_mcu_control;
+ API d_mcu_control_sema;
+
+ // Background tasks
+ API d_background_enable;
+ API d_background_abort;
+ API d_background_state;
+ API d_max_background;
+ API a_background_tasks[16];
+ API a_back_task_io[16];
+
+ // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory)
+ API d_gea_mode_ovly;
+ API a_gea_kc_ovly[4];
+
+ API d_hole3_ndb[8];
+
+ // Encryption module
+ API d_a5mode; // Encryption Mode.
+
+ API d_sched_mode_gprs_ovly;
+
+ // 7 words are reserved for any possible mapping modification
+ API d_hole4_ndb[5];
+
+ // Ramp definition for Omega device
+ API a_ramp[16];
+
+ // CCCH/SACCH downlink information...(!!)
+ API a_cd[15]; // Header + CCCH/SACCH downlink information.
+
+ // FACCH downlink information........(!!)
+ API a_fd[15]; // Header + FACCH downlink information.
+
+ // Traffic downlink data frames......(!!)
+ API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0.
+
+ // CCCH/SACCH uplink information.....(!!)
+ API a_cu[15]; // Header + CCCH/SACCH uplink information.
+
+ // FACCH downlink information........(!!)
+ API a_fu[15]; // Header + FACCH uplink information
+
+ // Traffic downlink data frames......(!!)
+ API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0.
+
+ // Random access.....................(MCU -> DSP).
+ API d_rach; // RACH information.
+
+ //...................................(MCU -> DSP).
+ API a_kc[4]; // Encryption Key Code.
+
+ // Integrated Data Services module
+ API d_ra_conf;
+ API d_ra_act;
+ API d_ra_test;
+ API d_ra_statu;
+ API d_ra_statd;
+ API d_fax;
+ API a_data_buf_ul[21];
+ API a_data_buf_dl[37];
+
+ #if (L1_NEW_AEC)
+ // new AEC
+ API a_new_aec_holes[422];
+ API d_cont_filter;
+ API d_granularity_att;
+ API d_coef_smooth;
+ API d_es_level_max;
+ API d_fact_vad;
+ API d_thrs_abs;
+ API d_fact_asd_fil;
+ API d_fact_asd_mut;
+ API d_far_end_pow_h;
+ API d_far_end_pow_l;
+ API d_far_end_noise_h;
+ API d_far_end_noise_l;
+ #endif
+
+ // Speech recognition model
+ #if (L1_NEW_AEC)
+ API a_sr_holes[1165];
+ #else
+ API a_sr_holes[1599];
+ #endif // L1_NEW_AEC
+ API a_model[1041];
+
+ // EOTD buffer
+ #if (L1_EOTD==1)
+ API d_eotd_first;
+ API d_eotd_max;
+ API d_eotd_nrj_high;
+ API d_eotd_nrj_low;
+ API a_eotd_crosscor[18];
+ #else
+ API a_eotd_holes[22];
+ #endif
+
+ #if (MELODY_E2)
+ API a_melody_e2_holes0[27];
+ API d_melody_e2_osc_used;
+ API d_melody_e2_osc_active;
+ API d_melody_e2_semaphore;
+ API a_melody_e2_osc[16][3];
+ API d_melody_e2_globaltimefactor;
+ API a_melody_e2_instrument_ptr[8];
+ API a_melody_e2_holes1[708];
+ API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE];
+ API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT];
+ #endif
+ }
+ T_NDB_MCU_DSP;
+
+#elif ((DSP == 32) || (DSP == 31))
+ typedef struct
+ {
+ // Monitoring tasks control..........(MCU <- DSP)
+ API d_fb_det; // FB detection result. (1 for FOUND).
+ API d_fb_mode; // Mode for FB detection algorithm.
+ API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR).
+
+ // CCCH/SACCH downlink information...(!!)
+ API a_cd[15]; // Header + CCCH/SACCH downlink information.
+
+ // FACCH downlink information........(!!)
+ API a_fd[15]; // Header + FACCH downlink information.
+
+ // Traffic downlink data frames......(!!)
+ API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0.
+ API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1.
+
+ // CCCH/SACCH uplink information.....(!!)
+ API a_cu[15]; // Header + CCCH/SACCH uplink information.
+
+ #if (SPEECH_RECO)
+ // FACCH downlink information........(!!)
+ API a_fu[3]; // Header + FACCH uplink information
+ // The size of this buffer is 15 word but some speech reco words
+ // are overlayer with this buffer. This is the reason why the size is 3 instead of 15.
+ API d_sr_status; // status of the DSP speech reco task
+ API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API sr_hole1; // hole
+ API d_sr_bit_exact_test; // bit exact test
+ API d_sr_nb_words; // number of words used in the speech recognition task
+ API d_sr_db_level; // estimate voice level in dB
+ API d_sr_db_noise; // estimate noise in dB
+ API d_sr_mod_size; // size of the model
+ API sr_holes_1[4]; // hole
+ #else
+ // FACCH downlink information........(!!)
+ API a_fu[15]; // Header + FACCH uplink information
+ #endif
+
+ // Traffic uplink data frames........(!!)
+ API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0.
+ API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1.
+
+ // Random access.....................(MCU -> DSP).
+ API d_rach; // RACH information.
+
+ //...................................(MCU -> DSP).
+ API d_a5mode; // Encryption Mode.
+ API a_kc[4]; // Encryption Key Code.
+ API d_tch_mode; // TCH mode register.
+ // bit [0..1] -> b_dai_mode.
+ // bit [2] -> b_dtx.
+
+ // OMEGA...........................(MCU -> DSP).
+ #if ((ANLG_FAM == 1) || (ANLG_FAM == 2))
+ API a_ramp[16];
+ #if (MELODY_E1)
+ API d_melo_osc_used;
+ API d_melo_osc_active;
+ API a_melo_note0[4];
+ API a_melo_note1[4];
+ API a_melo_note2[4];
+ API a_melo_note3[4];
+ API a_melo_note4[4];
+ API a_melo_note5[4];
+ API a_melo_note6[4];
+ API a_melo_note7[4];
+ #if (DSP==31)
+ // selection of the melody format
+ API d_melody_selection;
+ API holes[9];
+ #else // DSP==32
+ API d_dco_type; // Tide
+ API p_start_IQ;
+ API d_level_off;
+ API d_dco_dbg;
+ API d_tide_resa;
+ API d_asynch_margin; // Perseus Asynch Audio Workaround
+ API hole[4];
+ #endif // DSP 32
+
+ #else // NO MELODY E1
+ #if (DSP==31)
+ // selection of the melody format
+ API d_melody_selection;
+ API holes[43]; // 43 unused holes.
+ #else // DSP==32
+ API holes[34]; // 34 unused holes.
+ API d_dco_type; // Tide
+ API p_start_IQ;
+ API d_level_off;
+ API d_dco_dbg;
+ API d_tide_resa;
+ API d_asynch_margin; // Perseus Asynch Audio Workaround
+ API hole[4];
+ #endif //DSP == 32
+ #endif // NO MELODY E1
+
+ API d_debug3;
+ API d_debug2;
+ API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega
+ API d_afcctladd;
+ API d_vbuctrl;
+ API d_vbdctrl;
+ API d_apcdel1;
+ API d_aec_ctrl;
+ API d_apcoff;
+ API d_bulioff;
+ API d_bulqoff;
+ API d_dai_onoff;
+ API d_auxdac;
+
+ #if (ANLG_FAM == 1)
+ API d_vbctrl;
+ #elif (ANLG_FAM == 2)
+ API d_vbctrl1;
+ #endif
+
+ API d_bbctrl;
+ #else
+ #error DSPCODE not supported with given ANALOG
+ #endif //(ANALOG)1, 2
+ //...................................(MCU -> DSP).
+ API a_sch26[5]; // Header + SB information, array of 5 words.
+
+ // TONES.............................(MCU -> DSP)
+ API d_toneskb_init;
+ API d_toneskb_status;
+ API d_k_x1_t0;
+ API d_k_x1_t1;
+ API d_k_x1_t2;
+ API d_pe_rep;
+ API d_pe_off;
+ API d_se_off;
+ API d_bu_off;
+ API d_t0_on;
+ API d_t0_off;
+ API d_t1_on;
+ API d_t1_off;
+ API d_t2_on;
+ API d_t2_off;
+ API d_k_x1_kt0;
+ API d_k_x1_kt1;
+ API d_dur_kb;
+
+ // PLL...............................(MCU -> DSP).
+ API d_pll_clkmod1;
+ API d_pll_clkmod2;
+
+ // DSP status returned..........(DSP --> MCU).
+ API d_error_status;
+
+ // RIF control.......................(MCU -> DSP).
+ API d_spcx_rif;
+
+ API d_shiftdl;
+ API d_shiftul;
+
+ API p_saec_prog;
+ API p_aec_prog;
+ API p_spenh_prog;
+
+ API a_ovly[75];
+ API d_ra_conf;
+ API d_ra_act;
+ API d_ra_test;
+ API d_ra_statu;
+ API d_ra_statd;
+ API d_fax;
+ #if (SPEECH_RECO)
+ API a_data_buf_ul[3];
+ API a_n_best_words[4]; // array of the 4 best words
+ API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length)
+ API sr_holes_2[6];
+ API a_data_buf_dl[37];
+
+ API a_hole[24];
+
+ API d_sched_mode_gprs_ovly;
+
+ API fir_holes1[384];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+ API d_audio_init;
+ API d_audio_status;
+
+ API a_model[1041]; // array of the speech reco model
+ #else
+ API a_data_buf_ul[21];
+ API a_data_buf_dl[37];
+
+ API a_hole[24];
+
+ API d_sched_mode_gprs_ovly;
+
+ API fir_holes1[384];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+ API d_audio_init;
+ API d_audio_status;
+
+#if (L1_EOTD ==1)
+ API a_eotd_hole[369];
+
+ API d_eotd_first;
+ API d_eotd_max;
+ API d_eotd_nrj_high;
+ API d_eotd_nrj_low;
+ API a_eotd_crosscor[18];
+#endif
+ #endif
+ }
+ T_NDB_MCU_DSP;
+
+
+#else // OTHER DSP CODE like 17
+
+typedef struct
+{
+ // Monitoring tasks control..........(MCU <- DSP)
+ API d_fb_det; // FB detection result. (1 for FOUND).
+ API d_fb_mode; // Mode for FB detection algorithm.
+ API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR).
+
+ // CCCH/SACCH downlink information...(!!)
+ API a_cd[15]; // Header + CCCH/SACCH downlink information.
+
+ // FACCH downlink information........(!!)
+ API a_fd[15]; // Header + FACCH downlink information.
+
+ // Traffic downlink data frames......(!!)
+ #if (DATA14_4 == 0)
+ API a_dd_0[20]; // Header + DATA traffic downlink information, sub. chan. 0.
+ API a_dd_1[20]; // Header + DATA traffic downlink information, sub. chan. 1.
+ #endif
+ #if (DATA14_4 == 1)
+ API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0.
+ API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1.
+ #endif
+
+ // CCCH/SACCH uplink information.....(!!)
+ API a_cu[15]; // Header + CCCH/SACCH uplink information.
+
+ #if (SPEECH_RECO)
+ // FACCH downlink information........(!!)
+ API a_fu[3]; // Header + FACCH uplink information
+ // The size of this buffer is 15 word but some speech reco words
+ // are overlayer with this buffer. This is the reason why the size is 3 instead of 15.
+ API d_sr_status; // status of the DSP speech reco task
+ API d_sr_param; // paramters for the DSP speech reco task: OOV threshold.
+ API sr_hole1; // hole
+ API d_sr_bit_exact_test; // bit exact test
+ API d_sr_nb_words; // number of words used in the speech recognition task
+ API d_sr_db_level; // estimate voice level in dB
+ API d_sr_db_noise; // estimate noise in dB
+ API d_sr_mod_size; // size of the model
+ API sr_holes_1[4]; // hole
+ #else
+ // FACCH downlink information........(!!)
+ API a_fu[15]; // Header + FACCH uplink information
+ #endif
+
+ // Traffic uplink data frames........(!!)
+ #if (DATA14_4 == 0)
+ API a_du_0[20]; // Header + DATA traffic uplink information, sub. chan. 0.
+ API a_du_1[20]; // Header + DATA traffic uplink information, sub. chan. 1.
+ #endif
+ #if (DATA14_4 == 1)
+ API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0.
+ API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1.
+ #endif
+
+ // Random access.....................(MCU -> DSP).
+ API d_rach; // RACH information.
+
+ //...................................(MCU -> DSP).
+ API d_a5mode; // Encryption Mode.
+ API a_kc[4]; // Encryption Key Code.
+ API d_tch_mode; // TCH mode register.
+ // bit [0..1] -> b_dai_mode.
+ // bit [2] -> b_dtx.
+
+ // OMEGA...........................(MCU -> DSP).
+
+#if ((ANLG_FAM == 1) || (ANLG_FAM == 2))
+ API a_ramp[16];
+ #if (MELODY_E1)
+ API d_melo_osc_used;
+ API d_melo_osc_active;
+ API a_melo_note0[4];
+ API a_melo_note1[4];
+ API a_melo_note2[4];
+ API a_melo_note3[4];
+ API a_melo_note4[4];
+ API a_melo_note5[4];
+ API a_melo_note6[4];
+ API a_melo_note7[4];
+ #if (DSP == 17)
+ // selection of the melody format
+ API d_dco_type; // Tide
+ API p_start_IQ;
+ API d_level_off;
+ API d_dco_dbg;
+ API d_tide_resa;
+ API d_asynch_margin; // Perseus Asynch Audio Workaround
+ API hole[4];
+ #else
+ API d_melody_selection;
+ API holes[9];
+ #endif
+ #else // NO MELODY E1
+ // selection of the melody format
+ #if (DSP == 17)
+ API holes[34]; // 34 unused holes.
+ API d_dco_type; // Tide
+ API p_start_IQ;
+ API d_level_off;
+ API d_dco_dbg;
+ API d_tide_resa;
+ API d_asynch_margin; // Perseus Asynch Audio Workaround
+ API hole[4]
+ #else
+ // selection of the melody format
+ API d_melody_selection;
+ API holes[43]; // 43 unused holes.
+ #endif
+ #endif
+ API d_debug3;
+ API d_debug2;
+ API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega
+ API d_afcctladd;
+ API d_vbuctrl;
+ API d_vbdctrl;
+ API d_apcdel1;
+ API d_aec_ctrl;
+ API d_apcoff;
+ API d_bulioff;
+ API d_bulqoff;
+ API d_dai_onoff;
+ API d_auxdac;
+ #if (ANLG_FAM == 1)
+ API d_vbctrl;
+ #elif (ANLG_FAM == 2)
+ API d_vbctrl1;
+ #endif
+ API d_bbctrl;
+
+ #else
+ #error DSPCODE not supported with given ANALOG
+ #endif //(ANALOG)1, 2
+ //...................................(MCU -> DSP).
+ API a_sch26[5]; // Header + SB information, array of 5 words.
+
+ // TONES.............................(MCU -> DSP)
+ API d_toneskb_init;
+ API d_toneskb_status;
+ API d_k_x1_t0;
+ API d_k_x1_t1;
+ API d_k_x1_t2;
+ API d_pe_rep;
+ API d_pe_off;
+ API d_se_off;
+ API d_bu_off;
+ API d_t0_on;
+ API d_t0_off;
+ API d_t1_on;
+ API d_t1_off;
+ API d_t2_on;
+ API d_t2_off;
+ API d_k_x1_kt0;
+ API d_k_x1_kt1;
+ API d_dur_kb;
+
+ // PLL...............................(MCU -> DSP).
+ API d_pll_clkmod1;
+ API d_pll_clkmod2;
+
+ // DSP status returned..........(DSP --> MCU).
+ API d_error_status;
+
+ // RIF control.......................(MCU -> DSP).
+ API d_spcx_rif;
+
+ API d_shiftdl;
+ API d_shiftul;
+
+ #if (AEC == 1)
+ // AEC control.......................(MCU -> DSP).
+ #if (VOC == FR_EFR)
+ API p_aec_init;
+ API p_aec_prog;
+ API p_spenh_init;
+ API p_spenh_prog;
+ #endif
+
+ #if (VOC == FR_HR_EFR)
+ API p_saec_prog;
+ API p_aec_prog;
+ API p_spenh_prog;
+ #endif
+ #endif
+
+ API a_ovly[75];
+ API d_ra_conf;
+ API d_ra_act;
+ API d_ra_test;
+ API d_ra_statu;
+ API d_ra_statd;
+ API d_fax;
+ #if (SPEECH_RECO)
+ API a_data_buf_ul[3];
+ API a_n_best_words[4]; // array of the 4 best words
+ API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length)
+ API sr_holes_2[6];
+ API a_data_buf_dl[37];
+
+ API fir_holes1[409];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+ API d_audio_init;
+ API d_audio_status;
+ API a_model[1041]; // array of the speech reco model
+ #else
+ API a_data_buf_ul[21];
+ API a_data_buf_dl[37];
+
+ API fir_holes1[409];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+ API d_audio_init;
+ API d_audio_status;
+ #endif
+}
+T_NDB_MCU_DSP;
+#endif
+
+#if (DSP == 34) || (DSP == 35) || (DSP == 36)
+typedef struct
+{
+ API_SIGNED d_transfer_rate;
+
+ // Common GSM/GPRS
+ // These words specified the latencies to applies on some peripherics
+ API_SIGNED d_lat_mcu_bridge;
+ API_SIGNED d_lat_mcu_hom2sam;
+ API_SIGNED d_lat_mcu_bef_fast_access;
+ API_SIGNED d_lat_dsp_after_sam;
+
+ // DSP Start address
+ API_SIGNED d_gprs_install_address;
+
+ API_SIGNED d_misc_config;
+
+ API_SIGNED d_cn_sw_workaround;
+
+ API_SIGNED d_hole2_param[4];
+
+ //...................................Frequency Burst.
+ API_SIGNED d_fb_margin_beg;
+ API_SIGNED d_fb_margin_end;
+ API_SIGNED d_nsubb_idle;
+ API_SIGNED d_nsubb_dedic;
+ API_SIGNED d_fb_thr_det_iacq;
+ API_SIGNED d_fb_thr_det_track;
+ //...................................Demodulation.
+ API_SIGNED d_dc_off_thres;
+ API_SIGNED d_dummy_thres;
+ API_SIGNED d_dem_pond_gewl;
+ API_SIGNED d_dem_pond_red;
+
+ //...................................TCH Full Speech.
+ API_SIGNED d_maccthresh1;
+ API_SIGNED d_mldt;
+ API_SIGNED d_maccthresh;
+ API_SIGNED d_gu;
+ API_SIGNED d_go;
+ API_SIGNED d_attmax;
+ API_SIGNED d_sm;
+ API_SIGNED d_b;
+
+ // V42Bis module
+ API_SIGNED d_v42b_switch_hyst;
+ API_SIGNED d_v42b_switch_min;
+ API_SIGNED d_v42b_switch_max;
+ API_SIGNED d_v42b_reset_delay;
+
+ //...................................TCH Half Speech.
+ API_SIGNED d_ldT_hr;
+ API_SIGNED d_maccthresh_hr;
+ API_SIGNED d_maccthresh1_hr;
+ API_SIGNED d_gu_hr;
+ API_SIGNED d_go_hr;
+ API_SIGNED d_b_hr;
+ API_SIGNED d_sm_hr;
+ API_SIGNED d_attmax_hr;
+
+ //...................................TCH Enhanced FR Speech.
+ API_SIGNED c_mldt_efr;
+ API_SIGNED c_maccthresh_efr;
+ API_SIGNED c_maccthresh1_efr;
+ API_SIGNED c_gu_efr;
+ API_SIGNED c_go_efr;
+ API_SIGNED c_b_efr;
+ API_SIGNED c_sm_efr;
+ API_SIGNED c_attmax_efr;
+
+ //...................................CHED
+ API_SIGNED d_sd_min_thr_tchfs;
+ API_SIGNED d_ma_min_thr_tchfs;
+ API_SIGNED d_md_max_thr_tchfs;
+ API_SIGNED d_md1_max_thr_tchfs;
+
+ API_SIGNED d_sd_min_thr_tchhs;
+ API_SIGNED d_ma_min_thr_tchhs;
+ API_SIGNED d_sd_av_thr_tchhs;
+ API_SIGNED d_md_max_thr_tchhs;
+ API_SIGNED d_md1_max_thr_tchhs;
+
+ API_SIGNED d_sd_min_thr_tchefs;
+ API_SIGNED d_ma_min_thr_tchefs;
+ API_SIGNED d_md_max_thr_tchefs;
+ API_SIGNED d_md1_max_thr_tchefs;
+
+ API_SIGNED d_wed_fil_ini;
+ API_SIGNED d_wed_fil_tc;
+ API_SIGNED d_x_min;
+ API_SIGNED d_x_max;
+ API_SIGNED d_slope;
+ API_SIGNED d_y_min;
+ API_SIGNED d_y_max;
+ API_SIGNED d_wed_diff_threshold;
+ API_SIGNED d_mabfi_min_thr_tchhs;
+
+ // FACCH module
+ API_SIGNED d_facch_thr;
+
+ // IDS module
+ API_SIGNED d_max_ovsp_ul;
+ API_SIGNED d_sync_thres;
+ API_SIGNED d_idle_thres;
+ API_SIGNED d_m1_thres;
+ API_SIGNED d_max_ovsp_dl;
+ API_SIGNED d_gsm_bgd_mgt;
+
+ // FIR coefficients
+ API a_fir_holes[4];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+}
+T_PARAM_MCU_DSP;
+#elif (DSP == 33)
+typedef struct
+{
+ API_SIGNED d_transfer_rate;
+
+ // Common GSM/GPRS
+ // These words specified the latencies to applies on some peripherics
+ API_SIGNED d_lat_mcu_bridge;
+ API_SIGNED d_lat_mcu_hom2sam;
+ API_SIGNED d_lat_mcu_bef_fast_access;
+ API_SIGNED d_lat_dsp_after_sam;
+
+ // DSP Start address
+ API_SIGNED d_gprs_install_address;
+
+ API_SIGNED d_misc_config;
+
+ API_SIGNED d_cn_sw_workaround;
+
+ #if DCO_ALGO
+ API_SIGNED d_cn_dco_param;
+
+ API_SIGNED d_hole2_param[3];
+ #else
+ API_SIGNED d_hole2_param[4];
+ #endif
+
+ //...................................Frequency Burst.
+ API_SIGNED d_fb_margin_beg;
+ API_SIGNED d_fb_margin_end;
+ API_SIGNED d_nsubb_idle;
+ API_SIGNED d_nsubb_dedic;
+ API_SIGNED d_fb_thr_det_iacq;
+ API_SIGNED d_fb_thr_det_track;
+ //...................................Demodulation.
+ API_SIGNED d_dc_off_thres;
+ API_SIGNED d_dummy_thres;
+ API_SIGNED d_dem_pond_gewl;
+ API_SIGNED d_dem_pond_red;
+
+ //...................................TCH Full Speech.
+ API_SIGNED d_maccthresh1;
+ API_SIGNED d_mldt;
+ API_SIGNED d_maccthresh;
+ API_SIGNED d_gu;
+ API_SIGNED d_go;
+ API_SIGNED d_attmax;
+ API_SIGNED d_sm;
+ API_SIGNED d_b;
+
+ // V42Bis module
+ API_SIGNED d_v42b_switch_hyst;
+ API_SIGNED d_v42b_switch_min;
+ API_SIGNED d_v42b_switch_max;
+ API_SIGNED d_v42b_reset_delay;
+
+ //...................................TCH Half Speech.
+ API_SIGNED d_ldT_hr;
+ API_SIGNED d_maccthresh_hr;
+ API_SIGNED d_maccthresh1_hr;
+ API_SIGNED d_gu_hr;
+ API_SIGNED d_go_hr;
+ API_SIGNED d_b_hr;
+ API_SIGNED d_sm_hr;
+ API_SIGNED d_attmax_hr;
+
+ //...................................TCH Enhanced FR Speech.
+ API_SIGNED c_mldt_efr;
+ API_SIGNED c_maccthresh_efr;
+ API_SIGNED c_maccthresh1_efr;
+ API_SIGNED c_gu_efr;
+ API_SIGNED c_go_efr;
+ API_SIGNED c_b_efr;
+ API_SIGNED c_sm_efr;
+ API_SIGNED c_attmax_efr;
+
+ //...................................CHED
+ API_SIGNED d_sd_min_thr_tchfs;
+ API_SIGNED d_ma_min_thr_tchfs;
+ API_SIGNED d_md_max_thr_tchfs;
+ API_SIGNED d_md1_max_thr_tchfs;
+
+ API_SIGNED d_sd_min_thr_tchhs;
+ API_SIGNED d_ma_min_thr_tchhs;
+ API_SIGNED d_sd_av_thr_tchhs;
+ API_SIGNED d_md_max_thr_tchhs;
+ API_SIGNED d_md1_max_thr_tchhs;
+
+ API_SIGNED d_sd_min_thr_tchefs;
+ API_SIGNED d_ma_min_thr_tchefs;
+ API_SIGNED d_md_max_thr_tchefs;
+ API_SIGNED d_md1_max_thr_tchefs;
+
+ API_SIGNED d_wed_fil_ini;
+ API_SIGNED d_wed_fil_tc;
+ API_SIGNED d_x_min;
+ API_SIGNED d_x_max;
+ API_SIGNED d_slope;
+ API_SIGNED d_y_min;
+ API_SIGNED d_y_max;
+ API_SIGNED d_wed_diff_threshold;
+ API_SIGNED d_mabfi_min_thr_tchhs;
+
+ // FACCH module
+ API_SIGNED d_facch_thr;
+
+ // IDS module
+ API_SIGNED d_max_ovsp_ul;
+ API_SIGNED d_sync_thres;
+ API_SIGNED d_idle_thres;
+ API_SIGNED d_m1_thres;
+ API_SIGNED d_max_ovsp_dl;
+ API_SIGNED d_gsm_bgd_mgt;
+
+ // FIR coefficients
+ API a_fir_holes[4];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+}
+T_PARAM_MCU_DSP;
+
+#else
+
+typedef struct
+{
+ //...................................Frequency Burst.
+ API_SIGNED d_nsubb_idle;
+ API_SIGNED d_nsubb_dedic;
+ API_SIGNED d_fb_thr_det_iacq;
+ API_SIGNED d_fb_thr_det_track;
+ //...................................Demodulation.
+ API_SIGNED d_dc_off_thres;
+ API_SIGNED d_dummy_thres;
+ API_SIGNED d_dem_pond_gewl;
+ API_SIGNED d_dem_pond_red;
+ API_SIGNED hole[1];
+ API_SIGNED d_transfer_rate;
+ //...................................TCH Full Speech.
+ API_SIGNED d_maccthresh1;
+ API_SIGNED d_mldt;
+ API_SIGNED d_maccthresh;
+ API_SIGNED d_gu;
+ API_SIGNED d_go;
+ API_SIGNED d_attmax;
+ API_SIGNED d_sm;
+ API_SIGNED d_b;
+
+ #if (VOC == FR_HR) || (VOC == FR_HR_EFR)
+ //...................................TCH Half Speech.
+ API_SIGNED d_ldT_hr;
+ API_SIGNED d_maccthresh_hr;
+ API_SIGNED d_maccthresh1_hr;
+ API_SIGNED d_gu_hr;
+ API_SIGNED d_go_hr;
+ API_SIGNED d_b_hr;
+ API_SIGNED d_sm_hr;
+ API_SIGNED d_attmax_hr;
+ #endif
+
+ #if (VOC == FR_EFR) || (VOC == FR_HR_EFR)
+ //...................................TCH Enhanced FR Speech.
+ API_SIGNED c_mldt_efr;
+ API_SIGNED c_maccthresh_efr;
+ API_SIGNED c_maccthresh1_efr;
+ API_SIGNED c_gu_efr;
+ API_SIGNED c_go_efr;
+ API_SIGNED c_b_efr;
+ API_SIGNED c_sm_efr;
+ API_SIGNED c_attmax_efr;
+ #endif
+
+ //...................................TCH Full Speech.
+ API_SIGNED d_sd_min_thr_tchfs;
+ API_SIGNED d_ma_min_thr_tchfs;
+ API_SIGNED d_md_max_thr_tchfs;
+ API_SIGNED d_md1_max_thr_tchfs;
+
+ #if (VOC == FR) || (VOC == FR_HR) || (VOC == FR_HR_EFR)
+ //...................................TCH Half Speech.
+ API_SIGNED d_sd_min_thr_tchhs;
+ API_SIGNED d_ma_min_thr_tchhs;
+ API_SIGNED d_sd_av_thr_tchhs;
+ API_SIGNED d_md_max_thr_tchhs;
+ API_SIGNED d_md1_max_thr_tchhs;
+ #endif
+
+ #if (VOC == FR_EFR) || (VOC == FR_HR_EFR)
+ //...................................TCH Enhanced FR Speech.
+ API_SIGNED d_sd_min_thr_tchefs; //(24L *C_POND_RED)
+ API_SIGNED d_ma_min_thr_tchefs; //(1200L *C_POND_RED)
+ API_SIGNED d_md_max_thr_tchefs; //(2000L *C_POND_RED)
+ API_SIGNED d_md1_max_thr_tchefs; //(160L *C_POND_RED)
+ API_SIGNED d_hole1;
+ #endif
+
+ API_SIGNED d_wed_fil_ini;
+ API_SIGNED d_wed_fil_tc;
+ API_SIGNED d_x_min;
+ API_SIGNED d_x_max;
+ API_SIGNED d_slope;
+ API_SIGNED d_y_min;
+ API_SIGNED d_y_max;
+ API_SIGNED d_wed_diff_threshold;
+ API_SIGNED d_mabfi_min_thr_tchhs;
+ API_SIGNED d_facch_thr;
+ API_SIGNED d_dsp_test;
+
+
+ #if (DATA14_4 == 0 ) || (VOC == FR_HR_EFR)
+ API_SIGNED d_patch_addr1;
+ API_SIGNED d_patch_data1;
+ API_SIGNED d_patch_addr2;
+ API_SIGNED d_patch_data2;
+ API_SIGNED d_patch_addr3;
+ API_SIGNED d_patch_data3;
+ API_SIGNED d_patch_addr4;
+ API_SIGNED d_patch_data4;
+ #endif
+
+ //...................................
+ API_SIGNED d_version_number; // DSP patch version
+ API_SIGNED d_ti_version; // customer number. No more used since 1.5
+
+ API_SIGNED d_dsp_page;
+
+ #if IDS
+ API_SIGNED d_max_ovsp_ul;
+ API_SIGNED d_sync_thres;
+ API_SIGNED d_idle_thres;
+ API_SIGNED d_m1_thres;
+ API_SIGNED d_max_ovsp_dl;
+ #endif
+
+
+}
+T_PARAM_MCU_DSP;
+#endif
+
+#if (DSP_DEBUG_TRACE_ENABLE == 1)
+typedef struct
+{
+ API d_debug_ptr_begin;
+ API d_debug_ptr_end;
+}
+T_DB2_DSP_TO_MCU;
+#endif
+
+/* DSP error as per ndb->d_error_status */
+enum dsp_error {
+ DSP_ERR_RHEA = 0x0001,
+ DSP_ERR_IQ_SAMPLES = 0x0004,
+ DSP_ERR_DMA_PROG = 0x0008,
+ DSP_ERR_DMA_TASK = 0x0010,
+ DSP_ERR_DMA_PEND = 0x0020,
+ DSP_ERR_VM = 0x0080,
+ DSP_ERR_DMA_UL_TASK = 0x0100,
+ DSP_ERR_DMA_UL_PROG = 0x0200,
+ DSP_ERR_DMA_UL_PEND = 0x0400,
+ DSP_ERR_STACK_OV = 0x0800,
+};
+
+/* How an ABB register + value is expressed in the API RAM */
+#define ABB_VAL(reg, val) ( (((reg) & 0x1F) << 1) | (((val) & 0x3FF) << 6) )
+
+/* How an ABB register + value | TRUE is expressed in the API RAM */
+#define ABB_VAL_T(reg, val) (ABB_VAL(reg, val) | 1)
+
+#endif /* _CAL_DSP_API_H */
diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h
new file mode 100644
index 00000000..f2eae091
--- /dev/null
+++ b/src/target/firmware/include/calypso/du.h
@@ -0,0 +1,32 @@
+/* Calypso DU (Debug Unit) Driver */
+
+/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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.
+ *
+ */
+
+#ifndef _CALYPSO_DU_H
+#define _CALYPSO_DU_H
+
+#include <calypso/clock.h>
+
+void calypso_du_init();
+void calypso_du_stop();
+void calypsu_du_dump();
+
+#endif /* _CALYPSO_DU_H */
diff --git a/src/target/firmware/include/calypso/irq.h b/src/target/firmware/include/calypso/irq.h
new file mode 100644
index 00000000..5ea59797
--- /dev/null
+++ b/src/target/firmware/include/calypso/irq.h
@@ -0,0 +1,49 @@
+#ifndef _CALYPSO_IRQ_H
+#define _CALYPSO_IRQ_H
+
+enum irq_nr {
+ IRQ_WATCHDOG = 0,
+ IRQ_TIMER1 = 1,
+ IRQ_TIMER2 = 2,
+ IRQ_TSP_RX = 3,
+ IRQ_TPU_FRAME = 4,
+ IRQ_TPU_PAGE = 5,
+ IRQ_SIMCARD = 6,
+ IRQ_UART_MODEM = 7,
+ IRQ_KEYPAD_GPIO = 8,
+ IRQ_RTC_TIMER = 9,
+ IRQ_RTC_ALARM_I2C = 10,
+ IRQ_ULPD_GAUGING = 11,
+ IRQ_EXTERNAL = 12,
+ IRQ_SPI = 13,
+ IRQ_DMA = 14,
+ IRQ_API = 15,
+ IRQ_SIM_DETECT = 16,
+ IRQ_EXTERNAL_FIQ = 17,
+ IRQ_UART_IRDA = 18,
+ IRQ_ULPD_GSM_TIMER = 19,
+ IRQ_GEA = 20,
+ _NR_IRQ
+};
+
+typedef void irq_handler(enum irq_nr nr);
+
+/* initialize IRQ driver and enable interrupts */
+void irq_init(void);
+
+/* enable a certain interrupt */
+void irq_enable(enum irq_nr nr);
+
+/* disable a certain interrupt */
+void irq_disable(enum irq_nr nr);
+
+/* configure a certain interrupt */
+void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio);
+
+/* register an interrupt handler */
+void irq_register_handler(enum irq_nr nr, irq_handler *handler);
+
+/* Install the exception handlers to where the ROM loader jumps */
+void calypso_exceptions_install(void);
+
+#endif /* _CALYPSO_IRQ_H */
diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h
new file mode 100644
index 00000000..2d1f8d97
--- /dev/null
+++ b/src/target/firmware/include/calypso/l1_environment.h
@@ -0,0 +1,365 @@
+#include <stdint.h>
+
+typedef unsigned short API;
+typedef signed short API_SIGNED;
+
+#define FAR
+
+#define CHIPSET 12
+#define DSP 36
+#define ANLG_FAM 2 /* Iota */
+
+/* MFTAB */
+#define L1_MAX_FCT 5 /* Max number of fctions in a frame */
+#define MFTAB_SIZE 20
+
+#define NBMAX_CARRIER 174+374 /* Number of carriers (GSM-Ext + DCS */
+
+#define DPAGC_FIFO_LEN 4
+
+#define SIZE_HIST 10
+
+#if !L1_GPRS
+# define NBR_DL_L1S_TASKS 32
+#else
+# define NBR_DL_L1S_TASKS 45
+#endif
+
+#define NBR_L1A_PROCESSES 46
+
+#define W_A_DSP_IDLE3 1
+
+
+
+// Identifier for all DSP tasks.
+// ...RX & TX tasks identifiers.
+#define NO_DSP_TASK 0 // No task.
+#define NP_DSP_TASK 21 // Normal Paging reading task.
+#define EP_DSP_TASK 22 // Extended Paging reading task.
+#define NBS_DSP_TASK 19 // Normal BCCH serving reading task.
+#define EBS_DSP_TASK 20 // Extended BCCH serving reading task.
+#define NBN_DSP_TASK 17 // Normal BCCH neighbour reading task.
+#define EBN_DSP_TASK 18 // Extended BCCH neighbour reading task.
+#define ALLC_DSP_TASK 24 // CCCH reading task while performing FULL BCCH/CCCH reading task.
+#define CB_DSP_TASK 25 // CBCH reading task.
+#define DDL_DSP_TASK 26 // SDCCH/D (data) reading task.
+#define ADL_DSP_TASK 27 // SDCCH/A (SACCH) reading task.
+#define DUL_DSP_TASK 12 // SDCCH/D (data) transmit task.
+#define AUL_DSP_TASK 11 // SDCCH/A (SACCH) transmit task.
+#define RACH_DSP_TASK 10 // RACH transmit task.
+#define TCHT_DSP_TASK 13 // TCH Traffic data DSP task id (RX or TX)
+#define TCHA_DSP_TASK 14 // TCH SACCH data DSP task id (RX or TX)
+#define TCHD_DSP_TASK 28 // TCH Traffic data DSP task id (RX or TX)
+
+#define TCH_DTX_UL 15 // Replace UL task in DSP->MCU com. to say "burst not transmitted".
+
+#if (L1_GPRS)
+ // Identifier for DSP tasks Packet dedicated.
+ // ...RX & TX tasks identifiers.
+ //------------------------------------------------------------------------
+ // WARNING ... Need to aligned following macro with MCU/DSP GPRS Interface
+ //------------------------------------------------------------------------
+ #define PNP_DSP_TASK 30
+ #define PEP_DSP_TASK 31
+ #define PALLC_DSP_TASK 32
+ #define PBS_DSP_TASK 33
+
+ #define PTCCH_DSP_TASK 33
+
+#endif
+
+// Identifier for measurement, FB / SB search tasks.
+// Values 1,2,3 reserved for "number of measurements".
+#define FB_DSP_TASK 5 // Freq. Burst reading task in Idle mode.
+#define SB_DSP_TASK 6 // Sync. Burst reading task in Idle mode.
+#define TCH_FB_DSP_TASK 8 // Freq. Burst reading task in Dedicated mode.
+#define TCH_SB_DSP_TASK 9 // Sync. Burst reading task in Dedicated mode.
+#define IDLE1 1
+
+// Debug tasks
+#define CHECKSUM_DSP_TASK 33
+#define TST_NDB 35 // Checksum DSP->MCU
+#define TST_DB 36 // DB communication check
+#define INIT_VEGA 37
+#define DSP_LOOP_C 38
+
+// Identifier for measurement, FB / SB search tasks.
+// Values 1,2,3 reserved for "number of measurements".
+#define TCH_LOOP_A 31
+#define TCH_LOOP_B 32
+
+// bits in d_gsm_bgd_mgt - background task management
+#define B_DSPBGD_RECO 1 // start of reco in dsp background
+#define B_DSPBGD_UPD 2 // start of alignement update in dsp background
+#define B_DSPBGD_STOP_RECO 256 // stop of reco in dsp background
+#define B_DSPBGD_STOP_UPD 512 // stop of alignement update in dsp background
+
+// bit in d_pll_config
+#define B_32KHZ_CALIB (1 << 14) // force DSP in Idle1 during 32 khz calibration
+// ****************************************************************
+// NDB AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS
+// ****************************************************************
+// bits in d_tch_mode
+#define B_EOTD (1 << 0) // EOTD mode
+#define B_PLAY_UL (1 << 3) // Play UL
+#define B_DCO_ON (1 << 4) // DCO ON/OFF
+#define B_AUDIO_ASYNC (1 << 1) // WCP reserved
+
+// ****************************************************************
+// PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS
+// ****************************************************************
+#define C_POND_RED 1L
+// below values are defined in the file l1_time.h
+//#define D_NSUBB_IDLE 296L
+//#define D_NSUBB_DEDIC 30L
+#define D_FB_THR_DET_IACQ 0x3333L
+#define D_FB_THR_DET_TRACK 0x28f6L
+#define D_DC_OFF_THRES 0x7fffL
+#define D_DUMMY_THRES 17408L
+#define D_DEM_POND_GEWL 26624L
+#define D_DEM_POND_RED 20152L
+#define D_HOLE 0L
+#define D_TRANSFER_RATE 0x6666L
+
+// Full Rate vocoder definitions.
+#define D_MACCTHRESH1 7872L
+#define D_MLDT -4L
+#define D_MACCTHRESH 7872L
+#define D_GU 5772L
+#define D_GO 7872L
+#define D_ATTMAX 53L
+#define D_SM -892L
+#define D_B 208L
+#define D_SD_MIN_THR_TCHFS 15L //(24L *C_POND_RED)
+#define D_MA_MIN_THR_TCHFS 738L //(1200L *C_POND_RED)
+#define D_MD_MAX_THR_TCHFS 1700L //(2000L *C_POND_RED)
+#define D_MD1_MAX_THR_TCHFS 99L //(160L *C_POND_RED)
+
+#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36)
+ // Frequency burst definitions
+ #define D_FB_MARGIN_BEG 24
+ #define D_FB_MARGIN_END 22
+
+ // V42bis definitions
+ #define D_V42B_SWITCH_HYST 16L
+ #define D_V42B_SWITCH_MIN 64L
+ #define D_V42B_SWITCH_MAX 250L
+ #define D_V42B_RESET_DELAY 10L
+
+ // Latencies definitions
+ #if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36)
+ // C.f. BUG1404
+ #define D_LAT_MCU_BRIDGE 0x000FL
+ #else
+ #define D_LAT_MCU_BRIDGE 0x0009L
+ #endif
+
+ #define D_LAT_MCU_HOM2SAM 0x000CL
+
+ #define D_LAT_MCU_BEF_FAST_ACCESS 0x0005L
+ #define D_LAT_DSP_AFTER_SAM 0x0004L
+
+ // Background Task in GSM mode: Initialization.
+ #define D_GSM_BGD_MGT 0L
+
+#if (CHIPSET == 4)
+ #define D_MISC_CONFIG 0L
+#elif (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12)
+ #define D_MISC_CONFIG 1L
+#else
+ #define D_MISC_CONFIG 0L
+#endif
+
+#endif
+
+// Hall Rate vocoder and ched definitions.
+
+#define D_SD_MIN_THR_TCHHS 37L
+#define D_MA_MIN_THR_TCHHS 344L
+#define D_MD_MAX_THR_TCHHS 2175L
+#define D_MD1_MAX_THR_TCHHS 138L
+#define D_SD_AV_THR_TCHHS 1845L
+#define D_WED_FIL_TC 0x7c00L
+#define D_WED_FIL_INI 4650L
+#define D_X_MIN 15L
+#define D_X_MAX 23L
+#define D_Y_MIN 703L
+#define D_Y_MAX 2460L
+#define D_SLOPE 135L
+#define D_WED_DIFF_THRESHOLD 406L
+#define D_MABFI_MIN_THR_TCHHS 5320L
+#define D_LDT_HR -5
+#define D_MACCTRESH_HR 6500
+#define D_MACCTRESH1_HR 6500
+#define D_GU_HR 2620
+#define D_GO_HR 3700
+#define D_B_HR 182
+#define D_SM_HR -1608
+#define D_ATTMAX_HR 53
+
+// Enhanced Full Rate vocoder and ched definitions.
+
+#define C_MLDT_EFR -4
+#define C_MACCTHRESH_EFR 8000
+#define C_MACCTHRESH1_EFR 8000
+#define C_GU_EFR 4522
+#define C_GO_EFR 6500
+#define C_B_EFR 174
+#define C_SM_EFR -878
+#define C_ATTMAX_EFR 53
+#define D_SD_MIN_THR_TCHEFS 15L //(24L *C_POND_RED)
+#define D_MA_MIN_THR_TCHEFS 738L //(1200L *C_POND_RED)
+#define D_MD_MAX_THR_TCHEFS 1230L //(2000L *C_POND_RED)
+#define D_MD1_MAX_THR_TCHEFS 99L //(160L *C_POND_RED)
+
+
+// Integrated Data Services definitions.
+#define D_MAX_OVSPD_UL 8
+// Detect frames containing 90% of 1s as synchro frames
+#define D_SYNC_THRES 0x3f50
+// IDLE frames are only frames with 100 % of 1s
+#define D_IDLE_THRES 0x4000
+#define D_M1_THRES 5
+#define D_MAX_OVSP_DL 8
+
+// d_ra_act: bit field definition
+#define B_F48BLK 5
+
+// Mask for b_itc information (d_ra_conf)
+#define CE_MASK 0x04
+
+#define D_FACCH_THR 0
+#define D_DSP_TEST 0
+#define D_VERSION_NUMBER 0
+#define D_TI_VERSION 0
+
+
+/*------------------------------------------------------------------------------*/
+/* */
+/* DEFINITIONS FOR DSP <-> MCU COMMUNICATION. */
+/* ++++++++++++++++++++++++++++++++++++++++++ */
+/* */
+/*------------------------------------------------------------------------------*/
+// COMMUNICATION Interrupt definition
+//------------------------------------
+#define ALL_16BIT 0xffffL
+#define B_GSM_PAGE (1 << 0)
+#define B_GSM_TASK (1 << 1)
+#define B_MISC_PAGE (1 << 2)
+#define B_MISC_TASK (1 << 3)
+
+#define B_GSM_PAGE_MASK (ALL_16BIT ^ B_GSM_PAGE)
+#define B_GSM_TASK_MASK (ALL_16BIT ^ B_GSM_TASK)
+#define B_MISC_PAGE_MASK (ALL_16BIT ^ B_MISC_PAGE)
+#define B_MISC_TASK_MASK (ALL_16BIT ^ B_MISC_TASK)
+
+// Common definition
+//----------------------------------
+// Index to *_DEMOD* arrays.
+#define D_TOA 0 // Time Of Arrival.
+#define D_PM 1 // Power Measurement.
+#define D_ANGLE 2 // Angle (AFC correction)
+#define D_SNR 3 // Signal / Noise Ratio.
+
+// Bit name/position definitions.
+#define B_FIRE0 5 // Fire result bit 0. (00 -> NO ERROR) (01 -> ERROR CORRECTED)
+#define B_FIRE1 6 // Fire result bit 1. (10 -> ERROR) (11 -> unused)
+#define B_SCH_CRC 8 // CRC result for SB decoding. (1 for ERROR).
+#define B_BLUD 15 // Uplink,Downlink data block Present. (1 for PRESENT).
+#define B_AF 14 // Activity bit: 1 if data block is valid.
+#define B_BFI 2 // Bad Frame Indicator
+#define B_UFI 0 // UNRELIABLE FRAME Indicator
+#define B_ECRC 9 // Enhanced full rate CRC bit
+#define B_EMPTY_BLOCK 10 // for voice memo purpose, this bit is used to determine
+
+#if (DEBUG_DEDIC_TCH_BLOCK_STAT == 1)
+ #define FACCH_GOOD 10
+ #define FACCH_BAD 11
+#endif
+
+#if (AMR == 1)
+ // Place of the RX type in the AMR block header
+ #define RX_TYPE_SHIFT 3
+ #define RX_TYPE_MASK 0x0038
+
+ // Place of the vocoder type in the AMR block header
+ #define VOCODER_TYPE_SHIFT 0
+ #define VOCODER_TYPE_MASK 0x0007
+
+ // List of the possible RX types in a_dd block
+ #define SPEECH_GOOD 0
+ #define SPEECH_DEGRADED 1
+ #define ONSET 2
+ #define SPEECH_BAD 3
+ #define SID_FIRST 4
+ #define SID_UPDATE 5
+ #define SID_BAD 6
+ #define AMR_NO_DATA 7
+ #define AMR_INHIBIT 8
+
+ // List of possible RX types in RATSCCH block
+ #define C_RATSCCH_GOOD 5
+
+ // List of the possible AMR channel rate
+ #define AMR_CHANNEL_4_75 0
+ #define AMR_CHANNEL_5_15 1
+ #define AMR_CHANNEL_5_9 2
+ #define AMR_CHANNEL_6_7 3
+ #define AMR_CHANNEL_7_4 4
+ #define AMR_CHANNEL_7_95 5
+ #define AMR_CHANNEL_10_2 6
+ #define AMR_CHANNEL_12_2 7
+
+ // Types of RATSCCH blocks
+ #define C_RATSCCH_UNKNOWN 0
+ #define C_RATSCCH_CMI_PHASE_REQ 1
+ #define C_RATSCCH_AMR_CONFIG_REQ_MAIN 2
+ #define C_RATSCCH_AMR_CONFIG_REQ_ALT 3
+ #define C_RATSCCH_AMR_CONFIG_REQ_ALT_IGNORE 4 // Alternative AMR_CONFIG_REQ with updates coming in the next THRES_REQ block
+ #define C_RATSCCH_THRES_REQ 5
+
+ // These flags define a bitmap that indicates which AMR parameters are being modified by a RATSCCH
+ #define C_AMR_CHANGE_CMIP 0
+ #define C_AMR_CHANGE_ACS 1
+ #define C_AMR_CHANGE_ICM 2
+ #define C_AMR_CHANGE_THR1 3
+ #define C_AMR_CHANGE_THR2 4
+ #define C_AMR_CHANGE_THR3 5
+ #define C_AMR_CHANGE_HYST1 6
+ #define C_AMR_CHANGE_HYST2 7
+ #define C_AMR_CHANGE_HYST3 8
+
+ // CMIP default value
+ #define C_AMR_CMIP_DEFAULT 1 // According to ETSI specification 05.09, cmip is always 1 by default (new channel, handover...)
+
+#endif
+// "d_ctrl_tch" bits positions for TCH configuration.
+#define B_CHAN_MODE 0
+#define B_CHAN_TYPE 4
+#define B_RESET_SACCH 6
+#define B_VOCODER_ON 7
+#define B_SYNC_TCH_UL 8
+#if (AMR == 1)
+ #define B_SYNC_AMR 9
+#else
+#define B_SYNC_TCH_DL 9
+#endif
+#define B_STOP_TCH_UL 10
+#define B_STOP_TCH_DL 11
+#define B_TCH_LOOP 12
+#define B_SUBCHANNEL 15
+
+// "d_ctrl_abb" bits positions for conditionnal loading of abb registers.
+#define B_RAMP 0
+#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3))
+ #define B_BULRAMPDEL 3 // Note: this name is changed
+ #define B_BULRAMPDEL2 2 // Note: this name is changed
+ #define B_BULRAMPDEL_BIS 9
+ #define B_BULRAMPDEL2_BIS 10
+#endif
+#define B_AFC 4
+
+// "d_ctrl_system" bits positions.
+#define B_TSQ 0
+#define B_BCCH_FREQ_IND 3
+#define B_TASK_ABORT 15 // Abort RF tasks for DSP.
diff --git a/src/target/firmware/include/calypso/misc.h b/src/target/firmware/include/calypso/misc.h
new file mode 100644
index 00000000..4e480938
--- /dev/null
+++ b/src/target/firmware/include/calypso/misc.h
@@ -0,0 +1,8 @@
+#ifndef _CAL_MISC_H
+#define _CAL_MISC_H
+
+void memdump_range(unsigned int *ptr, unsigned int len);
+void dump_mem(void);
+void dump_dev_id(void);
+
+#endif /* _CAL_MISC_H */
diff --git a/src/target/firmware/include/calypso/rtc.h b/src/target/firmware/include/calypso/rtc.h
new file mode 100644
index 00000000..17528d00
--- /dev/null
+++ b/src/target/firmware/include/calypso/rtc.h
@@ -0,0 +1,6 @@
+#ifndef _CALYPSO_RTC_H
+#define _CALYPSO_RTC_H
+
+void rtc_init(void);
+
+#endif /* _CALYPSO_RTC_H */
diff --git a/src/target/firmware/include/calypso/timer.h b/src/target/firmware/include/calypso/timer.h
new file mode 100644
index 00000000..694e4ebc
--- /dev/null
+++ b/src/target/firmware/include/calypso/timer.h
@@ -0,0 +1,25 @@
+#ifndef _CAL_TIMER_H
+#define _CAL_TIMER_H
+
+/* Enable or Disable a timer */
+void hwtimer_enable(int num, int on);
+
+/* Configure pre-scaler and if timer is auto-reload */
+void hwtimer_config(int num, uint8_t pre_scale, int auto_reload);
+
+/* Load a timer with the given value */
+void hwtimer_load(int num, uint16_t val);
+
+/* Read the current timer value */
+uint16_t hwtimer_read(int num);
+
+/* Enable or disable the watchdog */
+void wdog_enable(int on);
+
+/* Reset cpu using watchdog */
+void wdog_reset(void);
+
+/* power up the timers */
+void hwtimer_init(void);
+
+#endif /* _CAL_TIMER_H */
diff --git a/src/target/firmware/include/calypso/tpu.h b/src/target/firmware/include/calypso/tpu.h
new file mode 100644
index 00000000..2db95aa1
--- /dev/null
+++ b/src/target/firmware/include/calypso/tpu.h
@@ -0,0 +1,122 @@
+#ifndef _CALYPSO_TPU_H
+#define _CALYPSO_TPU_H
+
+#define BITS_PER_TDMA 1250
+#define QBITS_PER_TDMA (BITS_PER_TDMA * 4) /* 5000 */
+#define TPU_RANGE QBITS_PER_TDMA
+#define SWITCH_TIME (TPU_RANGE-10)
+
+/* Assert or de-assert TPU reset */
+void tpu_reset(int active);
+/* Enable or Disable a new scenario loaded into the TPU */
+void tpu_enable(int active);
+/* Enable or Disable the clock of teh TPU Module */
+void tpu_clk_enable(int active);
+/* Enable Frame Interrupt generation on next frame. DSP will reset it */
+void tpu_dsp_frameirq_enable(void);
+/* Is a Frame interrupt still pending for the DSP ? */
+int tpu_dsp_fameirq_pending(void);
+/* Rewind the TPU, i.e. restart enqueueing instructions at the base addr */
+void tpu_rewind(void);
+/* Enqueue a raw TPU instruction */
+void tpu_enqueue(uint16_t instr);
+/* Initialize TPU and TPU driver */
+void tpu_init(void);
+/* (Busy)Wait until TPU is idle */
+void tpu_wait_idle(void);
+/* Enable FRAME interrupt generation */
+void tpu_frame_irq_en(int mcu, int dsp);
+/* Force the generation of a DSP interrupt */
+void tpu_force_dsp_frame_irq(void);
+
+/* Get the current TPU SYNCHRO register */
+uint16_t tpu_get_synchro(void);
+/* Get the current TPU OFFSET register */
+uint16_t tpu_get_offset(void);
+
+enum tpu_instr {
+ TPU_INSTR_AT = (1 << 13),
+ TPU_INSTR_OFFSET = (2 << 13),
+ TPU_INSTR_SYNCHRO = (3 << 13), /* Loading delta synchro value in TPU synchro register */
+ TPU_INSTR_WAIT = (5 << 13), /* Wait a certain period (in GSM qbits) */
+ TPU_INSTR_SLEEP = (0 << 13), /* Stop the sequencer by disabling TPU ENABLE bit in ctrl reg */
+ /* data processing */
+ TPU_INSTR_MOVE = (4 << 13),
+};
+
+/* Addresses internal to the TPU, only accessible via MOVE */
+enum tpu_reg_int {
+ TPUI_TSP_CTRL1 = 0x00,
+ TPUI_TSP_CTRL2 = 0x01,
+ TPUI_TX_1 = 0x04,
+ TPUI_TX_2 = 0x03,
+ TPUI_TX_3 = 0x02,
+ TPUI_TX_4 = 0x05,
+ TPUI_TSP_ACT_L = 0x06,
+ TPUI_TSP_ACT_U = 0x07,
+ TPUI_TSP_SET1 = 0x09,
+ TPUI_TSP_SET2 = 0x0a,
+ TPUI_TSP_SET3 = 0x0b,
+ TPUI_DSP_INT_PG = 0x10,
+ TPUI_GAUGING_EN = 0x11,
+};
+
+enum tpui_ctrl2_bits {
+ TPUI_CTRL2_RD = (1 << 0),
+ TPUI_CTRL2_WR = (1 << 1),
+};
+
+static inline uint16_t tpu_mod5000(int16_t time)
+{
+ if (time < 0)
+ return time + 5000;
+ if (time >= 5000)
+ return time - 5000;
+ return time;
+}
+
+/* Enqueue a SLEEP operation (stop sequencer by disabling TPU ENABLE bit) */
+static inline void tpu_enq_sleep(void)
+{
+ tpu_enqueue(TPU_INSTR_SLEEP);
+}
+
+/* Enqueue a MOVE operation */
+static inline void tpu_enq_move(uint8_t addr, uint8_t data)
+{
+ tpu_enqueue(TPU_INSTR_MOVE | (data << 5) | (addr & 0x1f));
+}
+
+/* Enqueue an AT operation */
+static inline void tpu_enq_at(int16_t time)
+{
+ tpu_enqueue(TPU_INSTR_AT | tpu_mod5000(time));
+}
+
+/* Enqueue a SYNC operation */
+static inline void tpu_enq_sync(int16_t time)
+{
+ tpu_enqueue(TPU_INSTR_SYNCHRO | time);
+}
+
+/* Enqueue a WAIT operation */
+static inline void tpu_enq_wait(int16_t time)
+{
+ tpu_enqueue(TPU_INSTR_WAIT | time);
+}
+
+/* Enqueue an OFFSET operation */
+static inline void tpu_enq_offset(int16_t time)
+{
+ tpu_enqueue(TPU_INSTR_OFFSET | time);
+}
+
+static inline void tpu_enq_dsp_irq(void)
+{
+ tpu_enq_move(TPUI_DSP_INT_PG, 0x0001);
+}
+
+/* add two numbers, modulo 5000, and ensure the result is positive */
+uint16_t add_mod5000(int16_t a, int16_t b);
+
+#endif /* _CALYPSO_TPU_H */
diff --git a/src/target/firmware/include/calypso/tsp.h b/src/target/firmware/include/calypso/tsp.h
new file mode 100644
index 00000000..0252f36e
--- /dev/null
+++ b/src/target/firmware/include/calypso/tsp.h
@@ -0,0 +1,30 @@
+#ifndef _CALYPSO_TSP_H
+#define _CALYPSO_TSP_H
+
+#define TSPACT(x) (1 << x)
+
+/* initiate a TSP write through the TPU */
+void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout);
+
+/* Configure clock edge and chip enable polarity for a device */
+void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge);
+
+/* Obtain the current tspact state */
+uint16_t tsp_act_state(void);
+
+/* Update the TSPACT state, including enable and disable */
+void tsp_act_update(uint16_t new_act);
+
+/* Enable one or multiple TSPACT signals */
+void tsp_act_enable(uint16_t bitmask);
+
+/* Disable one or multiple TSPACT signals */
+void tsp_act_disable(uint16_t bitmask);
+
+/* Toggle one or multiple TSPACT signals */
+void tsp_act_toggle(uint16_t bitmask);
+
+/* Initialize TSP driver */
+void tsp_init(void);
+
+#endif /* _CALYPSO_TSP_H */
diff --git a/src/target/firmware/include/calypso/uart.h b/src/target/firmware/include/calypso/uart.h
new file mode 100644
index 00000000..7eb925ed
--- /dev/null
+++ b/src/target/firmware/include/calypso/uart.h
@@ -0,0 +1,32 @@
+#ifndef _CAL_UART_H
+#define _CAL_UART_H
+
+#include <stdint.h>
+
+enum uart_baudrate {
+ UART_38400,
+ UART_57600,
+ UART_115200,
+ UART_230400,
+ UART_460800,
+ UART_614400,
+ UART_921600,
+};
+
+void uart_init(uint8_t uart, uint8_t interrupts);
+void uart_putchar_wait(uint8_t uart, int c);
+int uart_putchar_nb(uint8_t uart, int c);
+int uart_getchar_nb(uint8_t uart, uint8_t *ch);
+int uart_tx_busy(uint8_t uart);
+int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt);
+
+enum uart_irq {
+ UART_IRQ_TX_EMPTY,
+ UART_IRQ_RX_CHAR,
+};
+
+void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on);
+
+void uart_poll(uint8_t uart);
+
+#endif /* _CAL_UART_H */
diff --git a/src/target/firmware/include/cfi_flash.h b/src/target/firmware/include/cfi_flash.h
new file mode 100644
index 00000000..2ab8842a
--- /dev/null
+++ b/src/target/firmware/include/cfi_flash.h
@@ -0,0 +1,68 @@
+
+#ifndef _CFI_FLASH_H
+#define _CFI_FLASH_H
+
+#include <stdint.h>
+
+
+#define CFI_FLASH_MAX_ERASE_REGIONS 4
+
+/* structure of erase region descriptor */
+struct cfi_region {
+ uint16_t b_count;
+ uint16_t b_size;
+} __attribute__((packed));
+
+
+/* structure of cfi query response */
+struct cfi_query {
+ uint8_t qry[3];
+ uint16_t p_id;
+ uint16_t p_adr;
+ uint16_t a_id;
+ uint16_t a_adr;
+ uint8_t vcc_min;
+ uint8_t vcc_max;
+ uint8_t vpp_min;
+ uint8_t vpp_max;
+ uint8_t word_write_timeout_typ;
+ uint8_t buf_write_timeout_typ;
+ uint8_t block_erase_timeout_typ;
+ uint8_t chip_erase_timeout_typ;
+ uint8_t word_write_timeout_max;
+ uint8_t buf_write_timeout_max;
+ uint8_t block_erase_timeout_max;
+ uint8_t chip_erase_timeout_max;
+ uint8_t dev_size;
+ uint16_t interface_desc;
+ uint16_t max_buf_write_size;
+ uint8_t num_erase_regions;
+ struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
+} __attribute__((packed));
+
+typedef struct {
+ void *f_base;
+
+ uint32_t f_size;
+
+ uint16_t f_manuf_id;
+ uint16_t f_dev_id;
+
+ struct cfi_query f_query;
+} cfi_flash_t;
+
+typedef uint8_t flash_lock;
+
+void flash_init(cfi_flash_t *flash, void *base_addr);
+
+void flash_dump_info(cfi_flash_t *flash);
+
+flash_lock flash_block_getlock(cfi_flash_t *base_addr, uint32_t block_offset);
+
+void flash_block_unlock(cfi_flash_t *base_addr, uint32_t block_offset);
+void flash_block_lock(cfi_flash_t *base_addr, uint32_t block_offset);
+void flash_block_lockdown(cfi_flash_t *base_addr, uint32_t block_offset);
+
+void flash_block_erase(cfi_flash_t *base_addr, uint32_t block_addr);
+
+#endif
diff --git a/src/target/firmware/include/comm/msgb.h b/src/target/firmware/include/comm/msgb.h
new file mode 100644
index 00000000..3113fa2c
--- /dev/null
+++ b/src/target/firmware/include/comm/msgb.h
@@ -0,0 +1,161 @@
+#ifndef _MSGB_H
+#define _MSGB_H
+
+/* (C) 2008-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 <osmocore/linuxlist.h>
+#include <console.h>
+
+struct msgb {
+ struct llist_head list;
+
+ /* the layer 1 header, if any */
+ unsigned char *l1h;
+ /* the A-bis layer 2 header: OML, RSL(RLL), NS */
+ unsigned char *l2h;
+ /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
+ unsigned char *l3h;
+
+ uint16_t data_len;
+ uint16_t len;
+
+ unsigned char *head; /* start of buffer */
+ unsigned char *tail; /* end of message */
+ unsigned char *data; /* start of message */
+ unsigned char _data[0];
+};
+
+extern struct msgb *msgb_alloc(uint16_t size, const char *name);
+extern void msgb_free(struct msgb *m);
+extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
+extern struct msgb *msgb_dequeue(struct llist_head *queue);
+extern void msgb_reset(struct msgb *m);
+
+#define msgb_l1(m) ((void *)(m->l1h))
+#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l3(m) ((void *)(m->l3h))
+
+static inline unsigned int msgb_l1len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l1(msgb);
+}
+
+static inline unsigned int msgb_l2len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l2(msgb);
+}
+
+static inline unsigned int msgb_l3len(const struct msgb *msgb)
+{
+ return msgb->tail - (uint8_t *)msgb_l3(msgb);
+}
+
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+ return msgb->len - msgb->data_len;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->head + msgb->data_len) - msgb->tail;
+}
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+
+ /* we intentionally call cons_puts() here to display an allocation
+ * failure on the _other_ serial port (i.e. the one that doesn't
+ * have the HDLC layer on it */
+ if (msgb_tailroom(msgb) < len)
+ cons_puts("msgb_tailroom insufficient!\n");
+
+ msgb->tail += len;
+ msgb->len += len;
+ return tmp;
+}
+static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
+{
+ uint8_t *space = msgb_put(msgb, 1);
+ space[0] = word & 0xFF;
+}
+static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
+{
+ uint8_t *space = msgb_put(msgb, 2);
+ space[0] = word >> 8 & 0xFF;
+ space[1] = word & 0xFF;
+}
+static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
+{
+ uint8_t *space = msgb_put(msgb, 4);
+ space[0] = word >> 24 & 0xFF;
+ space[1] = word >> 16 & 0xFF;
+ space[2] = word >> 8 & 0xFF;
+ space[3] = word & 0xFF;
+}
+static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->data;
+ msgb->data += len;
+ msgb->len -= len;
+ return tmp;
+}
+static inline uint8_t msgb_get_u8(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 1);
+ return space[0];
+}
+static inline uint16_t msgb_get_u16(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 2);
+ return space[0] << 8 | space[1];
+}
+static inline uint32_t msgb_get_u32(struct msgb *msgb)
+{
+ uint8_t *space = msgb_get(msgb, 4);
+ return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3];
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+ msgb->data -= len;
+ msgb->len += len;
+ return msgb->data;
+}
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+ msgb->len -= len;
+ return msgb->data += len;
+}
+
+/* increase the headroom of an empty msgb, reducing the tailroom */
+static inline void msgb_reserve(struct msgb *msg, int len)
+{
+ msg->data += len;
+ msg->tail += len;
+}
+
+static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+ const char *name)
+{
+ struct msgb *msg = msgb_alloc(size, name);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+#endif /* _MSGB_H */
diff --git a/src/target/firmware/include/comm/sercomm.h b/src/target/firmware/include/comm/sercomm.h
new file mode 100644
index 00000000..8fbbff97
--- /dev/null
+++ b/src/target/firmware/include/comm/sercomm.h
@@ -0,0 +1,57 @@
+#ifndef _SERCOMM_H
+#define _SERCOMM_H
+
+/* SERCOMM layer on UART1 (modem UART) */
+
+#include <osmocore/msgb.h>
+
+#define SERCOMM_UART_NR 1
+
+#define HDLC_FLAG 0x7E
+#define HDLC_ESCAPE 0x7D
+
+#define HDLC_C_UI 0x03
+#define HDLC_C_P_BIT (1 << 4)
+#define HDLC_C_F_BIT (1 << 4)
+
+/* a low sercomm_dlci means high priority. A high DLCI means low priority */
+enum sercomm_dlci {
+ SC_DLCI_HIGHEST = 0,
+ SC_DLCI_DEBUG = 4,
+ SC_DLCI_L1A_L23 = 5,
+ SC_DLCI_LOADER = 9,
+ SC_DLCI_CONSOLE = 10,
+ SC_DLCI_ECHO = 128,
+ _SC_DLCI_MAX
+};
+
+void sercomm_init(void);
+int sercomm_initialized(void);
+
+/* User Interface: Tx */
+
+/* user interface for transmitting messages for a given DLCI */
+void sercomm_sendmsg(uint8_t dlci, struct msgb *msg);
+/* how deep is the Tx queue for a given DLCI */
+unsigned int sercomm_tx_queue_depth(uint8_t dlci);
+
+/* User Interface: Rx */
+
+/* receiving messages for a given DLCI */
+typedef void (*dlci_cb_t)(uint8_t dlci, struct msgb *msg);
+int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb);
+
+/* Driver Interface */
+
+/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */
+int sercomm_drv_pull(uint8_t *ch);
+/* the driver has received one byte, pass it into sercomm layer.
+ returns 1 in case of success, 0 in case of unrecognized char */
+int sercomm_drv_rx_char(uint8_t ch);
+
+static inline struct msgb *sercomm_alloc_msgb(unsigned int len)
+{
+ return msgb_alloc_headroom(len+4, 4, "sercomm_tx");
+}
+
+#endif /* _SERCOMM_H */
diff --git a/src/target/firmware/include/comm/sercomm_cons.h b/src/target/firmware/include/comm/sercomm_cons.h
new file mode 100644
index 00000000..11f66545
--- /dev/null
+++ b/src/target/firmware/include/comm/sercomm_cons.h
@@ -0,0 +1,10 @@
+#ifndef _SERCOMM_CONS_H
+#define _SERCOMM_CONS_H
+
+/* how large buffers do we allocate? */
+#define SERCOMM_CONS_ALLOC 256
+
+int sercomm_puts(const char *s);
+int sercomm_putchar(int c);
+
+#endif /* _SERCOMM_CONS_H */
diff --git a/src/target/firmware/include/comm/timer.h b/src/target/firmware/include/comm/timer.h
new file mode 100644
index 00000000..814d2c60
--- /dev/null
+++ b/src/target/firmware/include/comm/timer.h
@@ -0,0 +1,76 @@
+/*
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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.
+ *
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <sys/time.h>
+
+#include <osmocore/linuxlist.h>
+
+/**
+ * Timer management:
+ * - Create a struct timer_list
+ * - Fill out timeout and use add_timer or
+ * use schedule_timer to schedule a timer in
+ * x seconds and microseconds from now...
+ * - Use del_timer to remove the timer
+ *
+ * Internally:
+ * - We hook into select.c to give a timeval of the
+ * nearest timer. On already passed timers we give
+ * it a 0 to immediately fire after the select
+ * - update_timers will call the callbacks and remove
+ * the timers.
+ *
+ */
+struct timer_list {
+ struct llist_head entry;
+ unsigned long expires;
+
+ unsigned int active : 1;
+ unsigned int handled : 1;
+ unsigned int in_list : 1;
+
+ void (*cb)(void*);
+ void *data;
+};
+
+extern unsigned long volatile jiffies;
+
+/**
+ * timer management
+ */
+void add_timer(struct timer_list *timer);
+void schedule_timer(struct timer_list *timer, int miliseconds);
+void del_timer(struct timer_list *timer);
+int timer_pending(struct timer_list *timer);
+
+
+/**
+ * internal timer list management
+ */
+void prepare_timers(void);
+int update_timers(void);
+int timer_check(void);
+
+void timer_init(void);
+
+#endif
diff --git a/src/target/firmware/include/console.h b/src/target/firmware/include/console.h
new file mode 100644
index 00000000..7146e990
--- /dev/null
+++ b/src/target/firmware/include/console.h
@@ -0,0 +1,20 @@
+#ifndef _CONSOLE_H
+#define _CONSOLE_H
+
+/* This is the direct (IRQ driven) UART console, bypassing the HDLC layer.
+ * You should not need to call those functions unless you've decided to
+ * not use the HLDC layer or have a device with two UARTs */
+
+int cons_rb_append(const char *data, int len);
+int cons_puts(const char *s);
+int cons_putchar(char c);
+int cons_rb_flush(void);
+void cons_init(void);
+
+/* We want the console on UART 0 (IRDA UART) */
+#define CONS_UART_NR 0
+
+/* Size of the static ring-buffer that we keep for console print messages */
+#define CONS_RB_SIZE 4096
+
+#endif /* _CONSOLE_H */
diff --git a/src/target/firmware/include/ctors.h b/src/target/firmware/include/ctors.h
new file mode 100644
index 00000000..ee4c7b3e
--- /dev/null
+++ b/src/target/firmware/include/ctors.h
@@ -0,0 +1,16 @@
+#ifndef _CTORS_H
+#define _CTORS_H
+
+#if 0
+/* only supported by gcc 3.4 or later */
+#define __ctor_data __attribute__ ((constructor) (100))
+#define __ctor_board __attribute__ ((constructor) (200))
+#else
+#define __ctor_data __attribute__ ((constructor))
+#define __ctor_board __attribute__ ((constructor))
+#endif
+
+/* iterate over list of constructor functions and call each element */
+void do_global_ctors(const char *ctors_start, const char *ctors_end);
+
+#endif
diff --git a/src/target/firmware/include/ctype.h b/src/target/firmware/include/ctype.h
new file mode 100644
index 00000000..afa36392
--- /dev/null
+++ b/src/target/firmware/include/ctype.h
@@ -0,0 +1,54 @@
+#ifndef _LINUX_CTYPE_H
+#define _LINUX_CTYPE_H
+
+/*
+ * NOTE! This ctype does not handle EOF like the standard C
+ * library is required to.
+ */
+
+#define _U 0x01 /* upper */
+#define _L 0x02 /* lower */
+#define _D 0x04 /* digit */
+#define _C 0x08 /* cntrl */
+#define _P 0x10 /* punct */
+#define _S 0x20 /* white space (space/lf/tab) */
+#define _X 0x40 /* hex digit */
+#define _SP 0x80 /* hard space (0x20) */
+
+extern unsigned char _ctype[];
+
+#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
+
+#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
+#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
+#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
+#define isdigit(c) ((__ismask(c)&(_D)) != 0)
+#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
+#define islower(c) ((__ismask(c)&(_L)) != 0)
+#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
+#define ispunct(c) ((__ismask(c)&(_P)) != 0)
+#define isspace(c) ((__ismask(c)&(_S)) != 0)
+#define isupper(c) ((__ismask(c)&(_U)) != 0)
+#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)
+
+#define isascii(c) (((unsigned char)(c))<=0x7f)
+#define toascii(c) (((unsigned char)(c))&0x7f)
+
+static inline unsigned char __tolower(unsigned char c)
+{
+ if (isupper(c))
+ c -= 'A'-'a';
+ return c;
+}
+
+static inline unsigned char __toupper(unsigned char c)
+{
+ if (islower(c))
+ c -= 'a'-'A';
+ return c;
+}
+
+#define tolower(c) __tolower(c)
+#define toupper(c) __toupper(c)
+
+#endif
diff --git a/src/target/firmware/include/debug.h b/src/target/firmware/include/debug.h
new file mode 100644
index 00000000..27c4185d
--- /dev/null
+++ b/src/target/firmware/include/debug.h
@@ -0,0 +1,31 @@
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({ type __dummy; \
+ typeof(x) __dummy2; \
+ (void)(&__dummy == &__dummy2); \
+ 1; \
+})
+
+#ifdef DEBUG
+#define dputchar(x) putchar(x)
+#define dputs(x) puts(x)
+#define dphex(x,y) phex(x,y)
+#define printd(x, args ...) printf(x, ## args)
+#else
+#define dputchar(x)
+#define dputs(x)
+#define dphex(x,y)
+#define printd(x, args ...)
+#endif
+
+#endif /* _DEBUG_H */
diff --git a/src/target/firmware/include/defines.h b/src/target/firmware/include/defines.h
new file mode 100644
index 00000000..b3945163
--- /dev/null
+++ b/src/target/firmware/include/defines.h
@@ -0,0 +1,21 @@
+
+#ifndef _DEFINES_H
+#define _DEFINES_H
+
+#define __attribute_const__ __attribute__((__const__))
+
+/* type properties */
+#define __packed __attribute__((packed))
+#define __aligned(alignment) __attribute__((aligned(alignment)))
+#define __unused __attribute__((unused))
+
+/* linkage */
+#define __section(name) __attribute__((section(name)))
+
+/* force placement in zero-waitstate memory */
+/* XXX: these are placeholders */
+#define __fast_text
+#define __fast_data
+#define __fast_bss
+
+#endif /* !_DEFINES_H */
diff --git a/src/target/firmware/include/delay.h b/src/target/firmware/include/delay.h
new file mode 100644
index 00000000..0d6f3efd
--- /dev/null
+++ b/src/target/firmware/include/delay.h
@@ -0,0 +1,7 @@
+#ifndef delay_h
+#define delay_h
+
+void delay_ms(unsigned int ms);
+void delay_us(unsigned int us);
+
+#endif
diff --git a/src/target/firmware/include/display.h b/src/target/firmware/include/display.h
new file mode 100644
index 00000000..b49ae7bd
--- /dev/null
+++ b/src/target/firmware/include/display.h
@@ -0,0 +1,47 @@
+#ifndef _DISPLAY_DRIVER_H
+#define _DISPLAY_DRIVER_H
+
+enum display_attr {
+ DISP_ATTR_INVERT = 0x0001,
+};
+
+struct display_driver {
+ char *name;
+ void (*init)(void);
+ void (*set_attr)(unsigned long attr);
+ void (*unset_attr)(unsigned long attr);
+ void (*clrscr)(void);
+ void (*goto_xy)(int xpos, int ypos);
+ void (*set_color)(int fgcolor, int bgcolor);
+ int (*putc)(unsigned char c);
+ int (*puts)(const char *str);
+};
+
+extern struct display_driver *display;
+
+static inline void display_init(void)
+{
+ display->init();
+}
+static inline void display_set_attr(unsigned long attr)
+{
+ display->set_attr(attr);
+}
+static inline void display_unset_attr(unsigned long attr)
+{
+ display->unset_attr(attr);
+}
+static inline void display_clrscr(void)
+{
+ display->clrscr();
+}
+static inline int display_putchar(unsigned char c)
+{
+ return display->putc(c);
+}
+int display_puts(const char *s);
+
+extern const struct display_driver st7558_display;
+extern const struct display_driver ssd1783_display;
+
+#endif
diff --git a/src/target/firmware/include/display/ssd1783.h b/src/target/firmware/include/display/ssd1783.h
new file mode 100644
index 00000000..c72eebac
--- /dev/null
+++ b/src/target/firmware/include/display/ssd1783.h
@@ -0,0 +1,56 @@
+#ifndef _SSD1783_H
+#define _SSD1783_H
+
+/* Some basic colors */
+#define RED 0x0f00
+#define GREEN 0x00f0
+#define BLUE 0x000f
+#define YELLOW 0x0ff0
+#define MAGENTA 0x0f0f
+#define CYAN 0x00ff
+#define BLACK 0x0000
+#define WHITE 0x0fff
+
+/* Epson S1D15G10D08B000 commandset */
+#define CMD_DISON 0xaf // Display on
+#define CMD_DISOFF 0xae // Display off
+#define CMD_DISNOR 0xa6 // Normal display
+#define CMD_DISINV 0xa7 // Inverse display
+#define CMD_COMSCN 0xbb // Common scan direction
+#define CMD_DISCTL 0xca // Display control
+#define CMD_SLPIN 0x95 // Sleep in
+#define CMD_SLPOUT 0x94 // Sleep out
+#define CMD_PASET 0x75 // Page address set
+#define CMD_CASET 0x15 // Column address set
+#define CMD_DATCTL 0xbc // Data scan direction, etc.
+#define CMD_RGBSET8 0xce // 256-color position set
+#define CMD_RAMWR 0x5c // Writing to memory
+#define CMD_RAMRD 0x5d // Reading from memory
+#define CMD_PTLIN 0xa8 // Partial display in
+#define CMD_PTLOUT 0xa9 // Partial display out
+#define CMD_RMWIN 0xe0 // Read and modify write
+#define CMD_RMWOUT 0xee // End
+#define CMD_ASCSE 0xaa // Area scroll set
+#define CMD_SCSTART 0xab // Scroll start set
+#define CMD_OSCON 0xd1 // Internal oscillation on
+#define CMD_OSCOFF 0xd2 // Internal oscillation off
+#define CMD_PWRCTR 0x20 // Power control
+#define CMD_VOLCTR 0x81 // Electronic volume control
+#define CMD_VOLUP 0xd6 // Increment electronic control by 1
+#define CMD_VOLDOWN 0xd7 // Decrement electronic control by 1
+#define CMD_TMPGRD 0x82 // Temperature gradient set
+#define CMD_EPCTIN 0xcd // Control EEPROM
+#define CMD_EPCOUT 0xcc // Cancel EEPROM control
+#define CMD_EPMWR 0xfc // Write into EEPROM
+#define CMD_EPMRD 0xfd // Read from EEPROM
+#define CMD_EPSRRD1 0x7c // Read register 1
+#define CMD_EPSRRD2 0x7d // Read register 2
+#define CMD_NOP 0x25 // NOP instruction
+
+/* Extended SSD1783 commandset, partly (also has HW graphic functionalities) */
+#define CMD_BIASSET 0xfb // Set bias ratio
+#define CMD_FREQSET 0xf2 // Set frequency and n-line inversion
+#define CMD_RESCMD 0xa2 // reserved command
+#define CMD_PWMSEL 0xf7 // Select PWM/FRC, Full/8 color mode
+
+#endif
diff --git a/src/target/firmware/include/i2c.h b/src/target/firmware/include/i2c.h
new file mode 100644
index 00000000..37097a85
--- /dev/null
+++ b/src/target/firmware/include/i2c.h
@@ -0,0 +1,7 @@
+#ifndef _I2C_H
+#define _I2C_H
+
+int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len);
+void i2c_init(int speed, int slaveadd);
+
+#endif /* I2C_H */
diff --git a/src/target/firmware/include/keypad.h b/src/target/firmware/include/keypad.h
new file mode 100644
index 00000000..e2e6519f
--- /dev/null
+++ b/src/target/firmware/include/keypad.h
@@ -0,0 +1,66 @@
+#ifndef _KEYPAD_H
+#define _KEYPAD_H
+
+enum buttons {
+ BTN_0 = 0x00002000,
+ BTN_1 = 0x00008000,
+ BTN_2 = 0x00000400,
+ BTN_3 = 0x00000020,
+ BTN_4 = 0x00010000,
+ BTN_5 = 0x00000800,
+ BTN_6 = 0x00000040,
+ BTN_7 = 0x00020000,
+ BTN_8 = 0x00001000,
+ BTN_9 = 0x00000080,
+ BTN_STAR = 0x00040000,
+ BTN_HASH = 0x00000100,
+ BTN_MENU = 0x00004000,
+ BTN_LEFT_SB = 0x00080000,
+ BTN_RIGHT_SB = 0x00000200,
+ BTN_UP = 0x00000002,
+ BTN_DOWN = 0x00000004,
+ BTN_LEFT = 0x00000008,
+ BTN_RIGHT = 0x00000010,
+ BTN_OK = 0x00000001,
+ BTN_POWER = 0x01000000,
+};
+
+enum key_codes {
+ KEY_0 = 0,
+ KEY_1,
+ KEY_2,
+ KEY_3,
+ KEY_4,
+ KEY_5,
+ KEY_6,
+ KEY_7,
+ KEY_8,
+ KEY_9,
+ KEY_STAR, //*
+ KEY_HASH, //#
+ KEY_MENU, //center of directional keys
+ KEY_LEFT_SB, //softbutton
+ KEY_RIGHT_SB, //softbutton
+ KEY_UP,
+ KEY_DOWN,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_OK, //green off-hook
+ KEY_POWER, //red on-hook
+ KEY_INV = 0xFF
+};
+
+enum key_states {
+ PRESSED,
+ RELEASED,
+};
+
+void keypad_init(uint8_t interrupts);
+
+void keypad_poll();
+
+typedef void (*key_handler_t)(enum key_codes code, enum key_states state);
+
+void keypad_set_handler(key_handler_t handler);
+
+#endif /* KEYPAD_H */
diff --git a/src/target/firmware/include/layer1/afc.h b/src/target/firmware/include/layer1/afc.h
new file mode 100644
index 00000000..8b43f8aa
--- /dev/null
+++ b/src/target/firmware/include/layer1/afc.h
@@ -0,0 +1,18 @@
+#ifndef _L1_AFC_H
+#define _L1_AFC_H
+
+#define AFC_SNR_THRESHOLD 2560 /* 2.5 dB in fx6.10 */
+
+/* Input a frequency error sample into the AFC averaging */
+void afc_input(int32_t freq_error, uint16_t arfcn, int valid);
+
+/* Update the AFC with a frequency error, bypassing averaging */
+void afc_correct(int16_t freq_error, uint16_t arfcn);
+
+/* Update DSP with new AFC DAC value to be used for next TDMA frame */
+void afc_load_dsp(void);
+
+/* Reset the AFC to its initial DAC value */
+void afc_reset(void);
+
+#endif
diff --git a/src/target/firmware/include/layer1/agc.h b/src/target/firmware/include/layer1/agc.h
new file mode 100644
index 00000000..2b7e46e9
--- /dev/null
+++ b/src/target/firmware/include/layer1/agc.h
@@ -0,0 +1,7 @@
+#ifndef _L1_AGC_H
+#define _L1_AGC_H
+
+#define to_dbm8(x) ((x)*8)
+int16_t agc_inp_dbm8_by_pm(int16_t pm);
+
+#endif /* _L1_AGC_H */
diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h
new file mode 100644
index 00000000..03e33ca5
--- /dev/null
+++ b/src/target/firmware/include/layer1/async.h
@@ -0,0 +1,38 @@
+#ifndef _L1_ASYNC_H
+#define _L1_ASYNC_H
+
+#include <osmocore/msgb.h>
+
+#include <layer1/mframe_sched.h>
+
+/* When altering data structures used by L1 Sync part, we need to
+ * make sure to temporarily disable IRQ/FIQ to keep data consistent */
+static inline void l1a_lock_sync(void)
+{
+ arm_disable_interrupts();
+}
+
+static inline void l1a_unlock_sync(void)
+{
+ arm_enable_interrupts();
+}
+
+/* safely enable a message into the L1S TX queue */
+void l1a_txq_msgb_enq(struct llist_head *queue, struct msgb *msg);
+
+/* request a RACH request at the next multiframe T3 = fn51 */
+void l1a_rach_req(uint8_t fn51, uint8_t ra);
+
+/* Enable a repeating multiframe task */
+void l1a_mftask_enable(enum mframe_task task);
+
+/* Disable a repeating multiframe task */
+void l1a_mftask_disable(enum mframe_task task);
+
+/* Execute pending L1A completions */
+void l1a_compl_execute(void);
+
+/* Initialize asynchronous part of Layer1 */
+void l1a_init(void);
+
+#endif
diff --git a/src/target/firmware/include/layer1/avg.h b/src/target/firmware/include/layer1/avg.h
new file mode 100644
index 00000000..6c5de172
--- /dev/null
+++ b/src/target/firmware/include/layer1/avg.h
@@ -0,0 +1,23 @@
+#ifndef _L1_AVG_H
+#define _L1_AVG_H
+
+struct running_avg {
+ /* configuration */
+ uint16_t period; /* over how many samples to average */
+ uint16_t min_valid;
+
+ int32_t acc_val;
+ uint16_t num_samples; /* how often did we try to sample? */
+ uint16_t num_samples_valid; /* how often did we receive valid samples? */
+
+ void (*outfn)(struct running_avg *, int32_t avg);
+ void *priv;
+};
+
+/* input a new sample into the averaging process */
+void runavg_input(struct running_avg *ravg, int32_t val, int valid);
+
+/* check if sufficient samples have been obtained, and call outfn() */
+int runavg_check_output(struct running_avg *ravg);
+
+#endif /* _AVG_H */
diff --git a/src/target/firmware/include/layer1/l23_api.h b/src/target/firmware/include/layer1/l23_api.h
new file mode 100644
index 00000000..1ba6d173
--- /dev/null
+++ b/src/target/firmware/include/layer1/l23_api.h
@@ -0,0 +1,15 @@
+#ifndef _L1_L23_API_H
+#define _L1_L23_API_H
+
+#include <stdint.h>
+#include <osmocore/msgb.h>
+#include <l1a_l23_interface.h>
+
+void l1a_l23api_init(void);
+void l1_queue_for_l2(struct msgb *msg);
+struct msgb *l1ctl_msgb_alloc(uint8_t msg_type);
+struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, uint16_t arfcn);
+
+void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type);
+
+#endif /* _L1_L23_API_H */
diff --git a/src/target/firmware/include/layer1/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h
new file mode 100644
index 00000000..5e71daca
--- /dev/null
+++ b/src/target/firmware/include/layer1/mframe_sched.h
@@ -0,0 +1,58 @@
+#ifndef _L1_MFRAME_SCHED_H
+#define _L1_MFRAME_SCHED_H
+
+#include <stdint.h>
+
+enum mframe_task {
+ MF_TASK_BCCH_NORM,
+ MF_TASK_BCCH_EXT,
+ MF_TASK_CCCH,
+ MF_TASK_CCCH_COMB,
+
+ MF_TASK_SDCCH4_0,
+ MF_TASK_SDCCH4_1,
+ MF_TASK_SDCCH4_2,
+ MF_TASK_SDCCH4_3,
+
+ MF_TASK_SDCCH8_0,
+ MF_TASK_SDCCH8_1,
+ MF_TASK_SDCCH8_2,
+ MF_TASK_SDCCH8_3,
+ MF_TASK_SDCCH8_4,
+ MF_TASK_SDCCH8_5,
+ MF_TASK_SDCCH8_6,
+ MF_TASK_SDCCH8_7,
+
+ /* Test task: send Normal Burst in all timeslots */
+ MF_TASK_UL_ALL_NB,
+};
+
+enum mf_sched_item_flag {
+ MF_F_SACCH = (1 << 0),
+};
+
+/* The scheduler itself */
+struct mframe_scheduler {
+ uint32_t tasks;
+ uint32_t tasks_tgt;
+ uint32_t safe_fn;
+};
+
+uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts);
+
+/* Enable a specific task */
+void mframe_enable(enum mframe_task task_id);
+
+/* Disable a specific task */
+void mframe_disable(enum mframe_task task_id);
+
+/* Replace the current active set by the new one */
+void mframe_set(uint32_t tasks);
+
+/* Schedule mframe_sched_items according to current MF TASK list */
+void mframe_schedule(void);
+
+/* reset the scheduler, disabling all tasks */
+void mframe_reset(void);
+
+#endif /* _MFRAME_SCHED_H */
diff --git a/src/target/firmware/include/layer1/rfch.h b/src/target/firmware/include/layer1/rfch.h
new file mode 100644
index 00000000..344523c3
--- /dev/null
+++ b/src/target/firmware/include/layer1/rfch.h
@@ -0,0 +1,9 @@
+#ifndef _L1_RFCH_H
+#define _L1_RFCH_H
+
+struct gsm_time;
+
+void rfch_get_params(struct gsm_time *t,
+ uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p);
+
+#endif /* _L1_RFCH_H */
diff --git a/src/target/firmware/include/layer1/sched_gsmtime.h b/src/target/firmware/include/layer1/sched_gsmtime.h
new file mode 100644
index 00000000..630c6163
--- /dev/null
+++ b/src/target/firmware/include/layer1/sched_gsmtime.h
@@ -0,0 +1,24 @@
+#ifndef _L1_SCHED_GSMTIME_H
+#define _L1_SCHED_GSMTIME_H
+
+#include <stdint.h>
+#include <osmocore/linuxlist.h>
+
+struct sched_gsmtime_event {
+ struct llist_head list;
+ const struct tdma_sched_item *si;
+ uint32_t fn;
+ uint16_t p3; /* parameter for TDMA scheduler */
+};
+
+/* initialize the GSMTIME scheduler */
+void sched_gsmtime_init(void);
+
+/* Scheduling of a single event at a givnen GSM time */
+int sched_gsmtime(const struct tdma_sched_item *si, uint32_t fn, uint16_t p3);
+
+/* execute all GSMTIME one-shot events pending for 'current_fn' */
+int sched_gsmtime_execute(uint32_t current_fn);
+
+void sched_gsmtime_reset(void);
+#endif
diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h
new file mode 100644
index 00000000..760b44ce
--- /dev/null
+++ b/src/target/firmware/include/layer1/sync.h
@@ -0,0 +1,171 @@
+#ifndef _L1_SYNC_H
+#define _L1_SYNC_H
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/gsm_utils.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+#include <l1a_l23_interface.h>
+
+/* structure representing L1 sync information about a cell */
+struct l1_cell_info {
+ /* on which ARFCN (+band) is the cell? */
+ uint16_t arfcn;
+ /* what's the BSIC of the cell (from SCH burst decoding) */
+ 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
+ * compared to our current local frame number */
+ int32_t fn_offset;
+ /* how much does the TPU need adjustment (delta) to synchronize
+ * with the cells burst */
+ uint32_t time_alignment;
+ /* FIXME: should we also store the AFC value? */
+};
+
+enum l1s_chan {
+ L1S_CHAN_MAIN,
+ L1S_CHAN_SACCH,
+ _NUM_L1S_CHAN
+};
+
+enum l1_compl {
+ L1_COMPL_FB,
+ L1_COMPL_RACH,
+ L1_COMPL_TX_NB,
+};
+
+typedef void l1_compl_cb(enum l1_compl c);
+
+#define L1S_NUM_COMPL 32
+#define L1S_NUM_NEIGH_CELL 6
+
+struct l1s_state {
+ struct gsm_time current_time; /* current GSM time */
+ struct gsm_time next_time; /* GSM time at next TMDMA irq */
+
+ /* the cell on which we are camping right now */
+ struct l1_cell_info serving_cell;
+
+ /* neighbor cell sync info */
+ struct l1_cell_info neigh_cell[L1S_NUM_NEIGH_CELL];
+
+ /* TDMA scheduler */
+ struct tdma_scheduler tdma_sched;
+
+ /* Multiframe scheduler */
+ struct mframe_scheduler mframe_sched;
+
+ /* The current TPU offset register */
+ uint32_t tpu_offset;
+
+ /* Transmit queues of pending packets for main DCCH and ACCH */
+ struct llist_head tx_queue[_NUM_L1S_CHAN];
+
+ /* Which L1A completions are scheduled right now */
+ uint32_t scheduled_compl;
+ /* callbacks for each of the completions */
+ l1_compl_cb *completion[L1S_NUM_COMPL];
+
+ /* Structures below are for L1-task specific parameters, used
+ * to communicate between l1-sync and l1-async (l23_api) */
+ struct {
+ uint8_t mode; /* FB_MODE 0/1 */
+ } fb;
+
+ struct {
+ /* power measurement l1 task */
+ unsigned int mode;
+ union {
+ struct {
+ uint16_t arfcn_start;
+ uint16_t arfcn_next;
+ uint16_t arfcn_end;
+ } range;
+ };
+ struct msgb *msg;
+ } pm;
+
+ struct {
+ uint8_t ra;
+ } rach;
+
+ struct {
+ enum {
+ GSM_DCHAN_NONE = 0,
+ GSM_DCHAN_SDCCH_4,
+ GSM_DCHAN_SDCCH_8,
+ GSM_DCHAN_TCH_H,
+ GSM_DCHAN_TCH_F,
+ GSM_DCHAN_UNKNOWN,
+ } type;
+
+ uint8_t scn;
+ uint8_t tsc;
+ uint8_t tn;
+ uint8_t h;
+
+ union {
+ struct {
+ uint16_t arfcn;
+ } h0;
+ struct {
+ uint8_t hsn;
+ uint8_t maio;
+ uint8_t n;
+ uint16_t ma[64];
+ } h1;
+ };
+ } dedicated;
+};
+
+extern struct l1s_state l1s;
+
+struct l1s_meas_hdr {
+ uint16_t snr; /* signal/noise ratio */
+ int16_t toa_qbit; /* time of arrival (qbits) */
+ int16_t pm_dbm8; /* power level in dbm/8 */
+ int16_t freq_err; /* Frequency error in Hz */
+};
+
+int16_t l1s_snr_int(uint16_t snr);
+uint16_t l1s_snr_fract(uint16_t snr);
+
+void l1s_dsp_abort(void);
+
+void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode);
+void l1s_sb_test(uint8_t base_fn);
+void l1s_pm_test(uint8_t base_fn, uint16_t arfcn);
+void l1s_nb_test(uint8_t base_fn);
+
+/* schedule a completion */
+void l1s_compl_sched(enum l1_compl c);
+
+void l1s_init(void);
+
+/* reset the layer1 as part of synchronizing to a new cell */
+void l1s_reset(void);
+
+/* init.c */
+void layer1_init(void);
+
+/* A debug macro to print every TDMA frame */
+#ifdef DEBUG_EVERY_TDMA
+#define putchart(x) putchar(x)
+#else
+#define putchart(x)
+#endif
+
+/* Convert an angle in fx1.15 notatinon into Hz */
+#define BITFREQ_DIV_2PI 43104 /* 270kHz / 2 * pi */
+#define BITFREQ_DIV_PI 86208 /* 270kHz / pi */
+#define ANG2FREQ_SCALING (2<<15) /* 2^15 scaling factor for fx1.15 */
+#define ANGLE_TO_FREQ(angle) ((int16_t)angle * BITFREQ_DIV_PI / ANG2FREQ_SCALING)
+
+void l1s_reset_hw(void);
+void synchronize_tdma(struct l1_cell_info *cinfo);
+void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn);
+void l1s_time_dump(const struct gsm_time *time);
+
+#endif /* _L1_SYNC_H */
diff --git a/src/target/firmware/include/layer1/tdma_sched.h b/src/target/firmware/include/layer1/tdma_sched.h
new file mode 100644
index 00000000..9486c4ab
--- /dev/null
+++ b/src/target/firmware/include/layer1/tdma_sched.h
@@ -0,0 +1,62 @@
+#ifndef _L1_TDMA_SCHED_H
+#define _L1_TDMA_SCHED_H
+
+#include <stdint.h>
+
+/* TDMA scheduler */
+
+/* The idea of this scheduler is that we have a circular buffer of buckets,
+ * where each bucket corresponds to one future TDMA frame [interrupt]. Each
+ * bucket contains of a list of callbacks which are executed when the bucket
+ * index reaches that particular bucket. */
+
+#define TDMASCHED_NUM_FRAMES 25
+#define TDMASCHED_NUM_CB 5
+
+typedef int tdma_sched_cb(uint8_t p1, uint8_t p2, uint16_t p3);
+
+/* A single item in a TDMA scheduler bucket */
+struct tdma_sched_item {
+ tdma_sched_cb *cb;
+ uint8_t p1;
+ uint8_t p2;
+ uint16_t p3;
+};
+
+/* A bucket inside the TDMA scheduler */
+struct tdma_sched_bucket {
+ struct tdma_sched_item item[TDMASCHED_NUM_CB];
+ uint8_t num_items;
+};
+
+/* The scheduler itself, consisting of buckets and a current index */
+struct tdma_scheduler {
+ struct tdma_sched_bucket bucket[TDMASCHED_NUM_FRAMES];
+ uint8_t cur_bucket;
+};
+
+/* Schedule an item at 'frame_offset' TDMA frames in the future */
+int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint8_t p1, uint8_t p2, uint16_t p3);
+
+/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */
+int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint16_t p3);
+
+/* Execute pre-scheduled events for current frame */
+int tdma_sched_execute(void);
+
+/* Advance TDMA scheduler to the next bucket */
+void tdma_sched_advance(void);
+
+/* reset the scheduler; erase all scheduled items */
+void tdma_sched_reset(void);
+
+/* debug function: print number of entries of all TDMA buckets */
+void tdma_sched_dump(void);
+
+
+extern int tdma_end_set(uint8_t p1, uint8_t p2, uint16_t p3);
+#define SCHED_ITEM(x, y, z) { .cb = x, .p1 = y, .p2 = z }
+#define SCHED_END_FRAME() { .cb = NULL, .p1 = 0, .p2 = 0 }
+#define SCHED_END_SET() { .cb = &tdma_end_set, .p1 = 0, .p2 = 0 }
+
+#endif /* _L1_TDMA_SCHED_H */
diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h
new file mode 100644
index 00000000..09856c7e
--- /dev/null
+++ b/src/target/firmware/include/layer1/tpu_window.h
@@ -0,0 +1,23 @@
+#ifndef _L1_TPU_CTRL_H
+#define _L1_TPU_CTRL_H
+
+enum l1_rxwin_type {
+ L1_RXWIN_PW, /* power measurement */
+ L1_RXWIN_FB, /* FCCH burst detection */
+ L1_RXWIN_SB, /* SCH burst detection */
+ L1_RXWIN_NB, /* Normal burst decoding */
+ _NUM_L1_RXWIN
+};
+
+enum l1_txwin_type {
+ L1_TXWIN_NB, /* Normal burst sending */
+ L1_TXWIN_AB, /* RACH burst sending */
+ _NUM_L1_TXWIN
+};
+
+void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn);
+void l1s_tx_win_ctrl(uint16_t arfcn, enum l1_txwin_type wtype, uint8_t pwr, uint8_t tn);
+
+void tpu_end_scenario(void);
+
+#endif /* _L1_TPU_CTRL_H */
diff --git a/src/target/firmware/include/manifest.h b/src/target/firmware/include/manifest.h
new file mode 100644
index 00000000..6c1b2026
--- /dev/null
+++ b/src/target/firmware/include/manifest.h
@@ -0,0 +1,10 @@
+
+#ifndef _MANIFEST_H
+#define _MANIFEST_H
+
+extern const char *manifest_application;
+extern const char *manifest_revision;
+extern const char *manifest_board;
+extern const char *manifest_environment;
+
+#endif /* !_MANIFEST_H */
diff --git a/src/target/firmware/include/memory.h b/src/target/firmware/include/memory.h
new file mode 100644
index 00000000..b0a0490c
--- /dev/null
+++ b/src/target/firmware/include/memory.h
@@ -0,0 +1,28 @@
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#define __arch_getb(a) (*(volatile unsigned char *)(a))
+#define __arch_getw(a) (*(volatile unsigned short *)(a))
+#define __arch_getl(a) (*(volatile unsigned int *)(a))
+
+#define __arch_putb(v,a) (*(volatile unsigned char *)(a) = (v))
+#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v))
+#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v))
+
+#define __raw_writeb(v,a) __arch_putb(v,a)
+#define __raw_writew(v,a) __arch_putw(v,a)
+#define __raw_writel(v,a) __arch_putl(v,a)
+
+#define __raw_readb(a) __arch_getb(a)
+#define __raw_readw(a) __arch_getw(a)
+#define __raw_readl(a) __arch_getl(a)
+
+#define writeb(v,a) __arch_putb(v,a)
+#define writew(v,a) __arch_putw(v,a)
+#define writel(v,a) __arch_putl(v,a)
+
+#define readb(a) __arch_getb(a)
+#define readw(a) __arch_getw(a)
+#define readl(a) __arch_getl(a)
+
+#endif /* _MEMORY_H */
diff --git a/src/target/firmware/include/rf/trf6151.h b/src/target/firmware/include/rf/trf6151.h
new file mode 100644
index 00000000..c7951c0c
--- /dev/null
+++ b/src/target/firmware/include/rf/trf6151.h
@@ -0,0 +1,50 @@
+#ifndef _TRF6151_H
+#define _TRF6151_H
+
+#include <osmocore/gsm_utils.h>
+
+/* minimum gain (FE = LOW, VGA at lowest setting */
+#define TRF6151_GAIN_MIN 14
+/* minimum gain (FE = HIGH, VGA at highest setting */
+#define TRF6151_GAIN_MAX 60
+/* Frontend gain if FE = HIGH */
+#define TRF6151_GAIN_FE 20
+
+/* initialize (reset + power up) */
+void trf6151_init(void);
+
+/* switch power off or on */
+void trf6151_power(int on);
+
+/* set the VGA and RF gain */
+int trf6151_set_gain(uint8_t dbm, int high);
+
+/* obtain the current total gain of the TRF6151 */
+uint8_t trf6151_get_gain(void);
+
+/* Request the PLL to be tuned to the given frequency */
+void trf6151_set_arfcn(uint16_t arfcn, int uplink);
+
+enum trf6151_mode {
+ TRF6151_IDLE,
+ TRF6151_RX,
+ TRF6151_TX,
+};
+
+/* Set the operational mode of the TRF6151 chip */
+void trf6151_set_mode(enum trf6151_mode mode);
+
+void trf6151_test(uint16_t arfcn);
+void trf6151_tx_test(uint16_t arfcn);
+
+/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */
+void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn);
+
+/* prepare a Tx window with the TRF6151 finished at time 'start' (in qbits) */
+void trf6151_tx_window(int16_t start_qbits, uint16_t arfcn);
+
+/* Given the expected input level of exp_inp dBm and the target of target_bb
+ * dBm, configure the RF Frontend with the respective gain */
+void trf6151_compute_gain(int16_t exp_inp, int16_t target_bb);
+
+#endif /* TRF6151_H */
diff --git a/src/target/firmware/include/rffe.h b/src/target/firmware/include/rffe.h
new file mode 100644
index 00000000..950e597e
--- /dev/null
+++ b/src/target/firmware/include/rffe.h
@@ -0,0 +1,19 @@
+#ifndef _RFFE_H
+#define _RFFE_H
+
+#include <osmocore/gsm_utils.h>
+
+extern const uint8_t system_inherent_gain;
+
+/* initialize RF Frontend */
+void rffe_init(void);
+
+/* switch RF Frontend Mode */
+void rffe_mode(enum gsm_band band, int tx);
+
+/* get current gain of RF frontend (anything between antenna and baseband in dBm */
+uint8_t rffe_get_gain(void);
+
+void rffe_set_gain(int16_t exp_inp, int16_t target_bb);
+
+#endif
diff --git a/src/target/firmware/include/spi.h b/src/target/firmware/include/spi.h
new file mode 100644
index 00000000..0925a9a3
--- /dev/null
+++ b/src/target/firmware/include/spi.h
@@ -0,0 +1,7 @@
+#ifndef _SPI_H
+#define _SPI_H
+
+void spi_init(void);
+int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din);
+
+#endif /* _SPI_H */
diff --git a/src/target/firmware/include/stdint.h b/src/target/firmware/include/stdint.h
new file mode 100644
index 00000000..96907992
--- /dev/null
+++ b/src/target/firmware/include/stdint.h
@@ -0,0 +1,29 @@
+#ifndef OSMO_STDINT_H
+#define OSMO_STDINT_H
+
+/* some older toolchains (like gnuarm-3.x) don't provide a C99
+ compliant stdint.h yet, so we define our own here */
+
+/* to make matters worse newer gcc with glibc headers have
+ a incompatible definition of these types. We will use the
+ gcc'ism of #include_next to include the compiler's libc
+ header file and then check if it has defined int8_t and
+ if not we will use our own typedefs */
+
+#include_next <stdint.h>
+
+#ifndef __int8_t_defined
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif
+
+#endif
diff --git a/src/target/firmware/include/stdio.h b/src/target/firmware/include/stdio.h
new file mode 100644
index 00000000..15ed6688
--- /dev/null
+++ b/src/target/firmware/include/stdio.h
@@ -0,0 +1,52 @@
+#ifndef _STDIO_H
+#define _STDIO_H
+
+#ifndef NULL
+#define NULL 0
+#endif /* NULL */
+
+#include <sys/types.h>
+
+int printf(const char *format, ...);
+int sprintf(char *str, const char *format, ...);
+int snprintf(char *str, size_t size, const char *format, ...);
+
+#include <stdarg.h>
+
+int vprintf(const char *format, va_list ap);
+int vsprintf(char *str, const char *format, va_list ap);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+int puts(const char *s);
+
+#if 0
+/* start.S based uart console */
+#include <calypso/uart.h>
+#define putchar(c) uart_putchar_wait(1, c)
+int puts(const char *s);
+#endif
+
+#if 0
+/* regular UART console */
+#include <console.h>
+#define putchar(c) cons_putchar(c)
+#define _puts(s) cons_puts(s)
+#define ARCH_HAS_CONSOLE
+#endif
+
+#if 1
+/* sercomm based console */
+#include <comm/sercomm_cons.h>
+#define putchar(c) sercomm_putchar(c)
+#define _puts(s) sercomm_puts(s)
+#define ARCH_HAS_CONSOLE
+#endif
+
+struct __file {
+};
+
+typedef struct __file FILE;
+
+/* non-standard */
+extern void phex(unsigned int c, unsigned int len);
+
+#endif /* _STDIO_H */
diff --git a/src/target/firmware/include/string.h b/src/target/firmware/include/string.h
new file mode 100644
index 00000000..f060659a
--- /dev/null
+++ b/src/target/firmware/include/string.h
@@ -0,0 +1,12 @@
+#ifndef _STRING_H
+#define _STRING_H
+
+#include <sys/types.h>
+
+size_t strnlen(const char *s, size_t count);
+size_t strlen(const char *s);
+
+void *memset(void *s, int c, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+
+#endif
diff --git a/src/target/firmware/include/swab.h b/src/target/firmware/include/swab.h
new file mode 100644
index 00000000..61be900d
--- /dev/null
+++ b/src/target/firmware/include/swab.h
@@ -0,0 +1,297 @@
+#ifndef _LINUX_SWAB_H
+#define _LINUX_SWAB_H
+
+#include <stdint.h>
+#include <defines.h>
+#include <asm/swab.h>
+
+/*
+ * casts are necessary for constants, because we never know how for sure
+ * how U/UL/ULL map to uint16_t, uint32_t, uint64_t. At least not in a portable way.
+ */
+#define ___constant_swab16(x) ((uint16_t)( \
+ (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \
+ (((uint16_t)(x) & (uint16_t)0xff00U) >> 8)))
+
+#define ___constant_swab32(x) ((uint32_t)( \
+ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
+
+#define ___constant_swab64(x) ((uint64_t)( \
+ (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \
+ (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
+ (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
+ (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
+ (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
+ (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
+ (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
+ (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56)))
+
+#define ___constant_swahw32(x) ((uint32_t)( \
+ (((uint32_t)(x) & (uint32_t)0x0000ffffUL) << 16) | \
+ (((uint32_t)(x) & (uint32_t)0xffff0000UL) >> 16)))
+
+#define ___constant_swahb32(x) ((uint32_t)( \
+ (((uint32_t)(x) & (uint32_t)0x00ff00ffUL) << 8) | \
+ (((uint32_t)(x) & (uint32_t)0xff00ff00UL) >> 8)))
+
+/*
+ * Implement the following as inlines, but define the interface using
+ * macros to allow constant folding when possible:
+ * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32
+ */
+
+static inline __attribute_const__ uint16_t __fswab16(uint16_t val)
+{
+#ifdef __arch_swab16
+ return __arch_swab16(val);
+#else
+ return ___constant_swab16(val);
+#endif
+}
+
+static inline __attribute_const__ uint32_t __fswab32(uint32_t val)
+{
+#ifdef __arch_swab32
+ return __arch_swab32(val);
+#else
+ return ___constant_swab32(val);
+#endif
+}
+
+static inline __attribute_const__ uint64_t __fswab64(uint64_t val)
+{
+#ifdef __arch_swab64
+ return __arch_swab64(val);
+#elif defined(__SWAB_64_THRU_32__)
+ uint32_t h = val >> 32;
+ uint32_t l = val & ((1ULL << 32) - 1);
+ return (((uint64_t)__fswab32(l)) << 32) | ((uint64_t)(__fswab32(h)));
+#else
+ return ___constant_swab64(val);
+#endif
+}
+
+static inline __attribute_const__ uint32_t __fswahw32(uint32_t val)
+{
+#ifdef __arch_swahw32
+ return __arch_swahw32(val);
+#else
+ return ___constant_swahw32(val);
+#endif
+}
+
+static inline __attribute_const__ uint32_t __fswahb32(uint32_t val)
+{
+#ifdef __arch_swahb32
+ return __arch_swahb32(val);
+#else
+ return ___constant_swahb32(val);
+#endif
+}
+
+/**
+ * __swab16 - return a byteswapped 16-bit value
+ * @x: value to byteswap
+ */
+#define __swab16(x) \
+ (__builtin_constant_p((uint16_t)(x)) ? \
+ ___constant_swab16(x) : \
+ __fswab16(x))
+
+/**
+ * __swab32 - return a byteswapped 32-bit value
+ * @x: value to byteswap
+ */
+#define __swab32(x) \
+ (__builtin_constant_p((uint32_t)(x)) ? \
+ ___constant_swab32(x) : \
+ __fswab32(x))
+
+/**
+ * __swab64 - return a byteswapped 64-bit value
+ * @x: value to byteswap
+ */
+#define __swab64(x) \
+ (__builtin_constant_p((uint64_t)(x)) ? \
+ ___constant_swab64(x) : \
+ __fswab64(x))
+
+/**
+ * __swahw32 - return a word-swapped 32-bit value
+ * @x: value to wordswap
+ *
+ * __swahw32(0x12340000) is 0x00001234
+ */
+#define __swahw32(x) \
+ (__builtin_constant_p((uint32_t)(x)) ? \
+ ___constant_swahw32(x) : \
+ __fswahw32(x))
+
+/**
+ * __swahb32 - return a high and low byte-swapped 32-bit value
+ * @x: value to byteswap
+ *
+ * __swahb32(0x12345678) is 0x34127856
+ */
+#define __swahb32(x) \
+ (__builtin_constant_p((uint32_t)(x)) ? \
+ ___constant_swahb32(x) : \
+ __fswahb32(x))
+
+/**
+ * __swab16p - return a byteswapped 16-bit value from a pointer
+ * @p: pointer to a naturally-aligned 16-bit value
+ */
+static inline uint16_t __swab16p(const uint16_t *p)
+{
+#ifdef __arch_swab16p
+ return __arch_swab16p(p);
+#else
+ return __swab16(*p);
+#endif
+}
+
+/**
+ * __swab32p - return a byteswapped 32-bit value from a pointer
+ * @p: pointer to a naturally-aligned 32-bit value
+ */
+static inline uint32_t __swab32p(const uint32_t *p)
+{
+#ifdef __arch_swab32p
+ return __arch_swab32p(p);
+#else
+ return __swab32(*p);
+#endif
+}
+
+/**
+ * __swab64p - return a byteswapped 64-bit value from a pointer
+ * @p: pointer to a naturally-aligned 64-bit value
+ */
+static inline uint64_t __swab64p(const uint64_t *p)
+{
+#ifdef __arch_swab64p
+ return __arch_swab64p(p);
+#else
+ return __swab64(*p);
+#endif
+}
+
+/**
+ * __swahw32p - return a wordswapped 32-bit value from a pointer
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahw32() for details of wordswapping.
+ */
+static inline uint32_t __swahw32p(const uint32_t *p)
+{
+#ifdef __arch_swahw32p
+ return __arch_swahw32p(p);
+#else
+ return __swahw32(*p);
+#endif
+}
+
+/**
+ * __swahb32p - return a high and low byteswapped 32-bit value from a pointer
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahb32() for details of high/low byteswapping.
+ */
+static inline uint32_t __swahb32p(const uint32_t *p)
+{
+#ifdef __arch_swahb32p
+ return __arch_swahb32p(p);
+#else
+ return __swahb32(*p);
+#endif
+}
+
+/**
+ * __swab16s - byteswap a 16-bit value in-place
+ * @p: pointer to a naturally-aligned 16-bit value
+ */
+static inline void __swab16s(uint16_t *p)
+{
+#ifdef __arch_swab16s
+ __arch_swab16s(p);
+#else
+ *p = __swab16p(p);
+#endif
+}
+/**
+ * __swab32s - byteswap a 32-bit value in-place
+ * @p: pointer to a naturally-aligned 32-bit value
+ */
+static inline void __swab32s(uint32_t *p)
+{
+#ifdef __arch_swab32s
+ __arch_swab32s(p);
+#else
+ *p = __swab32p(p);
+#endif
+}
+
+/**
+ * __swab64s - byteswap a 64-bit value in-place
+ * @p: pointer to a naturally-aligned 64-bit value
+ */
+static inline void __swab64s(uint64_t *p)
+{
+#ifdef __arch_swab64s
+ __arch_swab64s(p);
+#else
+ *p = __swab64p(p);
+#endif
+}
+
+/**
+ * __swahw32s - wordswap a 32-bit value in-place
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahw32() for details of wordswapping
+ */
+static inline void __swahw32s(uint32_t *p)
+{
+#ifdef __arch_swahw32s
+ __arch_swahw32s(p);
+#else
+ *p = __swahw32p(p);
+#endif
+}
+
+/**
+ * __swahb32s - high and low byteswap a 32-bit value in-place
+ * @p: pointer to a naturally-aligned 32-bit value
+ *
+ * See __swahb32() for details of high and low byte swapping
+ */
+static inline void __swahb32s(uint32_t *p)
+{
+#ifdef __arch_swahb32s
+ __arch_swahb32s(p);
+#else
+ *p = __swahb32p(p);
+#endif
+}
+
+# define swab16 __swab16
+# define swab32 __swab32
+# define swab64 __swab64
+# define swahw32 __swahw32
+# define swahb32 __swahb32
+# define swab16p __swab16p
+# define swab32p __swab32p
+# define swab64p __swab64p
+# define swahw32p __swahw32p
+# define swahb32p __swahb32p
+# define swab16s __swab16s
+# define swab32s __swab32s
+# define swab64s __swab64s
+# define swahw32s __swahw32s
+# define swahb32s __swahb32s
+
+#endif /* _LINUX_SWAB_H */
diff --git a/src/target/firmware/include/uwire.h b/src/target/firmware/include/uwire.h
new file mode 100644
index 00000000..6d345534
--- /dev/null
+++ b/src/target/firmware/include/uwire.h
@@ -0,0 +1,7 @@
+#ifndef _UWIRE_H
+#define _UWIRE_H
+
+void uwire_init(void);
+int uwire_xfer(int cs, int bitlen, const void *dout, void *din);
+
+#endif /* _UWIRE_H */
diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile
new file mode 100644
index 00000000..dd3fcf1f
--- /dev/null
+++ b/src/target/firmware/layer1/Makefile
@@ -0,0 +1,8 @@
+
+LIBRARIES+=layer1
+layer1_DIR=layer1
+layer1_SRCS=avg.c agc.c afc.c sync.c tdma_sched.c tpu_window.c init.c l23_api.c \
+ mframe_sched.c sched_gsmtime.c async.c rfch.c
+
+layer1_SRCS += prim_pm.c prim_rach.c prim_tx_nb.c prim_rx_nb.c prim_fbsb.c
+
diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c
new file mode 100644
index 00000000..3890972d
--- /dev/null
+++ b/src/target/firmware/layer1/afc.c
@@ -0,0 +1,130 @@
+/* AFC (Automatic Frequency Correction) Implementation */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <debug.h>
+#include <osmocore/gsm_utils.h>
+
+#include <layer1/afc.h>
+#include <layer1/avg.h>
+#include <calypso/dsp.h>
+
+#define AFC_INITIAL_DAC_VALUE -700
+
+/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */
+#define AFC_PERIOD 40
+/* How many of our measurements have to be valid? */
+#define AFC_MIN_MUN_VALID 8
+
+/* The actual AFC code */
+
+struct afc_state {
+ struct running_avg ravg; /* running average */
+ int16_t dac_value; /* current DAC output value */
+ uint16_t arfcn;
+};
+
+static void afc_ravg_output(struct running_avg *ravg, int32_t avg);
+
+static struct afc_state afc_state = {
+ .ravg = {
+ .outfn = &afc_ravg_output,
+ .period = AFC_PERIOD,
+ .min_valid = AFC_MIN_MUN_VALID,
+ },
+ .dac_value = AFC_INITIAL_DAC_VALUE,
+};
+
+/* The AFC DAC in the ABB has to be configured as follows:
+ * DAC = 1MHz / 947MHz * FreqErr(Hz) / AFCslop(ppm/LSB)
+ * where:
+ * 947 MHz is the center of EGSM
+ * AFCslope is coded F1.15, thus a normalization factor of 2^15 aplpies
+ */
+
+#define AFC_NORM_FACTOR_GSM ((1<<15) / 947)
+#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894)
+
+/* we assume 8.769ppb per LSB, equals 0.008769 * 32768 == 287 */
+//#define AFC_SLOPE 320
+#define AFC_SLOPE 287
+
+/* The DSP can measure the frequency error in the following ranges:
+ * FB_MODE0: +/- 20 kHz
+ * FB_MODE1: +/- 4 kHz
+ * Sync Burst: +/- 1 kHz
+ * Normal Burst: +/- 400 Hz
+ */
+
+/* Update the AFC with a frequency error, bypassing averaging */
+void afc_correct(int16_t freq_error, uint16_t arfcn)
+{
+ int32_t afc_norm_factor;
+ int16_t delta;
+
+ switch (gsm_arfcn2band(arfcn)) {
+ case GSM_BAND_900:
+ case GSM_BAND_850:
+ afc_norm_factor = AFC_NORM_FACTOR_GSM;
+ break;
+ default:
+ afc_norm_factor = AFC_NORM_FACTOR_DCS;
+ }
+
+ delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE);
+ printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n",
+ freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta);
+ afc_state.dac_value += delta;
+
+ /* The AFC DAC has only 13 bits */
+ if (afc_state.dac_value > 4095)
+ afc_state.dac_value = 4095;
+ else if (afc_state.dac_value < -4096)
+ afc_state.dac_value = -4096;
+}
+
+void afc_reset(void)
+{
+ afc_state.dac_value = AFC_INITIAL_DAC_VALUE;
+}
+
+void afc_input(int32_t freq_error, uint16_t arfcn, int valid)
+{
+ afc_state.arfcn = arfcn;
+ runavg_input(&afc_state.ravg, freq_error, valid);
+ runavg_check_output(&afc_state.ravg);
+}
+
+/* callback function for runavg */
+static void afc_ravg_output(struct running_avg *ravg, int32_t avg)
+{
+ afc_correct(avg, afc_state.arfcn);
+}
+
+/* Update DSP with new AFC DAC value to be used for next TDMA frame */
+void afc_load_dsp(void)
+{
+ dsp_api.db_w->d_afc = afc_state.dac_value;
+ dsp_api.db_w->d_ctrl_abb |= (1 << B_AFC);
+}
diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c
new file mode 100644
index 00000000..780e260f
--- /dev/null
+++ b/src/target/firmware/layer1/agc.c
@@ -0,0 +1,62 @@
+/* AFC (Automatic Gain Control) Implementation */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <osmocore/gsm_utils.h>
+#include <debug.h>
+#include <rffe.h>
+
+#include <layer1/agc.h>
+#include <calypso/dsp.h>
+
+/* compute the input level present at the antenna based on a baseband
+ * power measurement of the DSP at baseband */
+int16_t agc_inp_dbm8_by_pm(int16_t pm)
+{
+ /* pm is in 1/8 dBm at baseband */
+ int16_t total_gain_dbm8;
+
+ /* compute total current gain */
+ total_gain_dbm8 = (system_inherent_gain + rffe_get_gain()) * 8;
+
+ /* subtract gain from power measurement at baseband level */
+ return pm - total_gain_dbm8;
+}
+
+uint8_t agc_il_by_dbm8(int16_t dbm8)
+{
+ uint16_t il;
+
+ /* convert from 1/8 dBm to l1c format: [220..0] in -1/2dBm unit */
+ if (dbm8 >= 0)
+ il = 0;
+ else
+ il = -dbm8;
+
+ /* saturate */
+ if (il > 4 * 255)
+ il = 4 * 255;
+
+ return (uint8_t)(il >> 2);
+}
diff --git a/src/target/firmware/layer1/async.c b/src/target/firmware/layer1/async.c
new file mode 100644
index 00000000..41f443ac
--- /dev/null
+++ b/src/target/firmware/layer1/async.c
@@ -0,0 +1,95 @@
+/* Asynchronous part of GSM Layer 1 */
+
+/* (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 <debug.h>
+#include <arm.h>
+#include <asm/system.h>
+
+#include <osmocore/msgb.h>
+
+#include <layer1/sync.h>
+#include <layer1/async.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/sched_gsmtime.h>
+#include <layer1/l23_api.h>
+
+extern const struct tdma_sched_item rach_sched_set_ul[];
+
+/* safely enable a message into the L1S TX queue */
+void l1a_txq_msgb_enq(struct llist_head *queue, struct msgb *msg)
+{
+ l1a_lock_sync();
+ msgb_enqueue(queue, msg);
+ l1a_unlock_sync();
+}
+
+/* Enable a repeating multiframe task */
+void l1a_mftask_enable(enum mframe_task task)
+{
+ /* we don't need locking here as L1S only reads mframe.tasks */
+ mframe_enable(task);
+}
+
+/* Disable a repeating multiframe task */
+void l1a_mftask_disable(enum mframe_task task)
+{
+ /* we don't need locking here as L1S only reads mframe.tasks */
+ mframe_disable(task);
+}
+
+/* Set the mask for repeating multiframe tasks */
+void l1a_mftask_set(uint32_t tasks)
+{
+ /* we don't need locking here as L1S only reads mframe.tasks */
+ mframe_set(tasks);
+}
+
+/* Initialize asynchronous part of Layer1 */
+void l1a_init(void)
+{
+ l1a_l23api_init();
+}
+
+/* Execute pending L1A completions */
+void l1a_compl_execute(void)
+{
+ unsigned long flags;
+ unsigned int scheduled;
+ unsigned int i;
+
+ /* get and reset the currently scheduled tasks */
+ local_firq_save(flags);
+ scheduled = l1s.scheduled_compl;
+ l1s.scheduled_compl = 0;
+ local_irq_restore(flags);
+
+ /* Iterate over list of scheduled completions, call their
+ * respective completion handler */
+ for (i = 0; i < 32; i++) {
+ if (!(scheduled & (1 << i)))
+ continue;
+ /* call completion function */
+ l1s.completion[i](i);
+ }
+}
diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c
new file mode 100644
index 00000000..a4bf565b
--- /dev/null
+++ b/src/target/firmware/layer1/avg.c
@@ -0,0 +1,57 @@
+/* Averaging Implementation */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+
+#include <layer1/avg.h>
+
+/* input a new sample into the averaging process */
+void runavg_input(struct running_avg *ravg, int32_t val, int valid)
+{
+ ravg->num_samples++;
+ if (valid) {
+ ravg->acc_val += val;
+ ravg->num_samples_valid++;
+ }
+}
+
+/* check if sufficient samples have been obtained, and call outfn() */
+int runavg_check_output(struct running_avg *ravg)
+{
+ if (ravg->num_samples < ravg->period)
+ return 0;
+
+ if (ravg->num_samples_valid >= ravg->min_valid) {
+ int32_t avg = ravg->acc_val / ravg->num_samples_valid;
+
+ ravg->outfn(ravg, avg);
+
+ ravg->num_samples = ravg->num_samples_valid = 0;
+ ravg->acc_val = 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c
new file mode 100644
index 00000000..7af327eb
--- /dev/null
+++ b/src/target/firmware/layer1/init.c
@@ -0,0 +1,74 @@
+/* OsmocomBB Layer1 initialization */
+
+/* (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 <rffe.h>
+#include <rf/trf6151.h>
+#include <abb/twl3025.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/irq.h>
+
+#include <layer1/sync.h>
+#include <layer1/async.h>
+#include <layer1/l23_api.h>
+
+void layer1_init(void)
+{
+#ifndef CONFIG_TX_ENABLE
+ printf("\n\nTHIS FIRMWARE WAS COMPILED WITHOUT TX SUPPORT!!!\n\n");
+#endif
+
+ /* initialize asynchronous part of L1 */
+ l1a_init();
+ /* initialize TDMA Frame IRQ driven synchronous L1 */
+ l1s_init();
+ /* power up the DSP */
+ dsp_power_on();
+
+ /* Initialize TPU, TSP and TRF drivers */
+ tpu_init();
+ tsp_init();
+ trf6151_init();
+
+ rffe_init();
+
+#if 0 /* only if RX TPU window is disabled! */
+ /* Put TWL3025 in downlink mode (includes calibration) */
+ twl3025_downlink(1, 1000);
+#endif
+
+ /* issue the TRF and TWL initialization sequence */
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+
+ /* Disable RTC interrupt as it causes lost TDMA frames */
+ irq_disable(IRQ_RTC_TIMER);
+
+ /* inform l2 and upwards that we are ready for orders */
+ l1ctl_tx_reset(L1CTL_RESET_IND, L1CTL_RES_T_BOOT);
+}
diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c
new file mode 100644
index 00000000..c1dbbb81
--- /dev/null
+++ b/src/target/firmware/layer1/l23_api.c
@@ -0,0 +1,395 @@
+/* Synchronous part of GSM Layer 1: API to Layer2+ */
+
+/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.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.
+ *
+ */
+
+#define DEBUG
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <debug.h>
+#include <byteorder.h>
+
+#include <osmocore/msgb.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/async.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/tpu_window.h>
+
+#include <rf/trf6151.h>
+
+#include <l1a_l23_interface.h>
+
+/* the size we will allocate struct msgb* for HDLC */
+#define L3_MSG_HEAD 4
+#define L3_MSG_SIZE (sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_data_ind) + L3_MSG_HEAD)
+
+void l1_queue_for_l2(struct msgb *msg)
+{
+ /* forward via serial for now */
+ sercomm_sendmsg(SC_DLCI_L1A_L23, msg);
+}
+
+static enum mframe_task chan_nr2mf_task(uint8_t chan_nr)
+{
+ uint8_t cbits = chan_nr >> 3;
+ uint8_t lch_idx;
+
+ if (cbits == 0x01) {
+ lch_idx = 0;
+ /* FIXME: TCH/F */
+ } else if ((cbits & 0x1e) == 0x02) {
+ lch_idx = cbits & 0x1;
+ /* FIXME: TCH/H */
+ } else if ((cbits & 0x1c) == 0x04) {
+ lch_idx = cbits & 0x3;
+ return MF_TASK_SDCCH4_0 + lch_idx;
+ } else if ((cbits & 0x18) == 0x08) {
+ lch_idx = cbits & 0x7;
+ return MF_TASK_SDCCH8_0 + lch_idx;
+#if 0
+ } else if (cbits == 0x10) {
+ /* FIXME: when to do extended BCCH? */
+ return MF_TASK_BCCH_NORM;
+ } else if (cbits == 0x11 || cbits == 0x12) {
+ /* FIXME: how to decide CCCH norm/extd? */
+ return MF_TASK_BCCH_CCCH;
+#endif
+ }
+ return 0;
+}
+
+static int chan_nr2dchan_type(uint8_t chan_nr)
+{
+ uint8_t cbits = chan_nr >> 3;
+
+ if (cbits == 0x01) {
+ return GSM_DCHAN_TCH_F;
+ } else if ((cbits & 0x1e) == 0x02) {
+ return GSM_DCHAN_TCH_H;
+ } else if ((cbits & 0x1c) == 0x04) {
+ return GSM_DCHAN_SDCCH_4;
+ } else if ((cbits & 0x18) == 0x08) {
+ return GSM_DCHAN_SDCCH_8;
+ }
+ return GSM_DCHAN_UNKNOWN;
+}
+
+struct msgb *l1ctl_msgb_alloc(uint8_t msg_type)
+{
+ struct msgb *msg;
+ struct l1ctl_hdr *l1h;
+
+ msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1ctl");
+ if (!msg) {
+ while (1) {
+ puts("OOPS. Out of buffers...\n");
+ }
+
+ return NULL;
+ }
+ l1h = (struct l1ctl_hdr *) msgb_put(msg, sizeof(*l1h));
+ l1h->msg_type = msg_type;
+ l1h->flags = 0;
+
+ msg->l1h = (uint8_t *)l1h;
+
+ return msg;
+}
+
+struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr,
+ uint16_t arfcn)
+{
+ struct l1ctl_info_dl *dl;
+ struct msgb *msg = l1ctl_msgb_alloc(msg_type);
+
+ dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl));
+ dl->frame_nr = htonl(fn);
+ dl->snr = snr;
+ dl->band_arfcn = htons(arfcn);
+
+ return msg;
+}
+
+/* receive a L1CTL_FBSB_REQ from L23 */
+static void l1ctl_rx_fbsb_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_fbsb_req *sync_req = (struct l1ctl_fbsb_req *) l1h->data;
+
+ if (sizeof(*sync_req) > msg->len) {
+ printf("Short sync msg. %u\n", msg->len);
+ return;
+ }
+
+ printd("L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n",
+ ntohs(sync_req->band_arfcn), sync_req->flags);
+
+ /* reset scheduler and hardware */
+ l1s_reset();
+
+ /* pre-set the CCCH mode */
+ l1s.serving_cell.ccch_mode = sync_req->ccch_mode;
+
+ printd("Starting FCCH Recognition\n");
+ l1s_fbsb_req(1, sync_req);
+}
+
+/* receive a L1CTL_DM_EST_REQ from L23 */
+static void l1ctl_rx_dm_est_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
+ struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload;
+
+ printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n",
+ ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc);
+
+ /* Current limitations */
+ if ((ul->chan_nr & 0x7) > 4) {
+ /* FIXME: Timeslot */
+ puts("We don't support TS > 4 yet\n");
+ return;
+ }
+
+ if ((chan_nr2mf_task(ul->chan_nr) >= MF_TASK_SDCCH8_4) &&
+ (chan_nr2mf_task(ul->chan_nr) <= MF_TASK_SDCCH8_7)) {
+ /* FIXME: TX while RX prevents SDCCH8 [4..7] */
+ puts("We don't support SDCCH8 [4..7] yet\n");
+ return;
+ }
+
+ /* configure dedicated channel state */
+ l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr);
+ l1s.dedicated.tsc = est_req->tsc;
+ l1s.dedicated.tn = ul->chan_nr & 0x7;
+ l1s.dedicated.h = est_req->h;
+
+ if (est_req->h) {
+ int i;
+ l1s.dedicated.h1.hsn = est_req->h1.hsn;
+ l1s.dedicated.h1.maio = est_req->h1.maio;
+ l1s.dedicated.h1.n = est_req->h1.n;
+ for (i=0; i<est_req->h1.n; i++)
+ l1s.dedicated.h1.ma[i] = ntohs(est_req->h1.ma[i]);
+ } else {
+ l1s.dedicated.h0.arfcn = ntohs(est_req->h0.band_arfcn);
+ }
+
+ /* figure out which MF tasks to enable */
+ l1a_mftask_set(1 << chan_nr2mf_task(ul->chan_nr));
+}
+
+/* receive a L1CTL_DM_REL_REQ from L23 */
+static void l1ctl_rx_dm_rel_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
+
+ l1a_mftask_set(0);
+}
+
+/* receive a L1CTL_RACH_REQ from L23 */
+static void l1ctl_rx_rach_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
+ struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload;
+
+ printd("L1CTL_RACH_REQ (ra=0x%02x, fn51=%d)\n", rach_req->ra, rach_req->fn51);
+
+ l1a_rach_req(rach_req->fn51, rach_req->ra);
+}
+
+/* receive a L1CTL_DATA_REQ from L23 */
+static void l1ctl_rx_data_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
+ struct l1ctl_data_ind *data_ind = (struct l1ctl_data_ind *) ul->payload;
+ struct llist_head *tx_queue;
+
+ printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul->link_id);
+
+ msg->l3h = data_ind->data;
+ tx_queue = (ul->link_id & 0x40) ?
+ &l1s.tx_queue[L1S_CHAN_SACCH] :
+ &l1s.tx_queue[L1S_CHAN_MAIN];
+
+ printd("ul=%p, ul->payload=%p, data_ind=%p, data_ind->data=%p l3h=%p\n",
+ ul, ul->payload, data_ind, data_ind->data, msg->l3h);
+
+ l1a_txq_msgb_enq(tx_queue, msg);
+}
+
+/* receive a L1CTL_PM_REQ from L23 */
+static void l1ctl_rx_pm_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data;
+
+ switch (pm_req->type) {
+ case 1:
+ l1s.pm.mode = 1;
+ l1s.pm.range.arfcn_start =
+ ntohs(pm_req->range.band_arfcn_from);
+ l1s.pm.range.arfcn_next =
+ ntohs(pm_req->range.band_arfcn_from);
+ l1s.pm.range.arfcn_end =
+ ntohs(pm_req->range.band_arfcn_to);
+ printf("L1CTL_PM_REQ start=%u end=%u\n",
+ l1s.pm.range.arfcn_start, l1s.pm.range.arfcn_end);
+ break;
+ }
+
+ l1s_pm_test(1, l1s.pm.range.arfcn_next);
+}
+
+/* Transmit a L1CTL_RESET_IND or L1CTL_RESET_CONF */
+void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type)
+{
+ struct msgb *msg = l1ctl_msgb_alloc(msg_type);
+ struct l1ctl_reset *reset_resp;
+ reset_resp = (struct l1ctl_reset *)
+ msgb_put(msg, sizeof(*reset_resp));
+ reset_resp->type = reset_type;
+
+ l1_queue_for_l2(msg);
+}
+
+/* receive a L1CTL_RESET_REQ from L23 */
+static void l1ctl_rx_reset_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_reset *reset_req =
+ (struct l1ctl_reset *) l1h->data;
+
+ switch (reset_req->type) {
+ case L1CTL_RES_T_FULL:
+ printf("L1CTL_RESET_REQ: FULL!\n");
+ l1s_reset();
+ l1s_reset_hw();
+ l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
+ break;
+ default:
+ printf("unknown L1CTL_RESET_REQ type\n");
+ break;
+ }
+}
+
+/* Transmit a L1CTL_CCCH_MODE_CONF */
+static void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode)
+{
+ struct msgb *msg = l1ctl_msgb_alloc(L1CTL_CCCH_MODE_CONF);
+ struct l1ctl_ccch_mode_conf *mode_conf;
+ mode_conf = (struct l1ctl_ccch_mode_conf *)
+ msgb_put(msg, sizeof(*mode_conf));
+ mode_conf->ccch_mode = ccch_mode;
+
+ l1_queue_for_l2(msg);
+}
+
+/* receive a L1CTL_CCCH_MODE_REQ from L23 */
+static void l1ctl_rx_ccch_mode_req(struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ struct l1ctl_ccch_mode_req *ccch_mode_req =
+ (struct l1ctl_ccch_mode_req *) l1h->data;
+ uint8_t ccch_mode = ccch_mode_req->ccch_mode;
+
+ /* pre-set the CCCH mode */
+ l1s.serving_cell.ccch_mode = ccch_mode;
+
+ /* Update task */
+ mframe_disable(MF_TASK_CCCH_COMB);
+ mframe_disable(MF_TASK_CCCH);
+
+ if (ccch_mode == CCCH_MODE_COMBINED)
+ mframe_enable(MF_TASK_CCCH_COMB);
+ else if (ccch_mode == CCCH_MODE_NON_COMBINED)
+ mframe_enable(MF_TASK_CCCH);
+
+ l1ctl_tx_ccch_mode_conf(ccch_mode);
+}
+
+/* callback from SERCOMM when L2 sends a message to L1 */
+static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg)
+{
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+
+#if 0
+ {
+ int i;
+ printf("l1a_l23_rx_cb (%u): ", msg->len);
+ for (i = 0; i < msg->len; i++)
+ printf("%02x ", msg->data[i]);
+ puts("\n");
+ }
+#endif
+
+ msg->l1h = msg->data;
+
+ if (sizeof(*l1h) > msg->len) {
+ printf("l1a_l23_cb: Short message. %u\n", msg->len);
+ goto exit_msgbfree;
+ }
+
+ switch (l1h->msg_type) {
+ case L1CTL_FBSB_REQ:
+ l1ctl_rx_fbsb_req(msg);
+ break;
+ case L1CTL_DM_EST_REQ:
+ l1ctl_rx_dm_est_req(msg);
+ break;
+ case L1CTL_DM_REL_REQ:
+ l1ctl_rx_dm_rel_req(msg);
+ break;
+ case L1CTL_RACH_REQ:
+ l1ctl_rx_rach_req(msg);
+ break;
+ case L1CTL_DATA_REQ:
+ l1ctl_rx_data_req(msg);
+ /* we have to keep the msgb, not free it! */
+ goto exit_nofree;
+ case L1CTL_PM_REQ:
+ l1ctl_rx_pm_req(msg);
+ break;
+ case L1CTL_RESET_REQ:
+ l1ctl_rx_reset_req(msg);
+ break;
+ case L1CTL_CCCH_MODE_REQ:
+ l1ctl_rx_ccch_mode_req(msg);
+ break;
+ }
+
+exit_msgbfree:
+ msgb_free(msg);
+exit_nofree:
+ return;
+}
+
+void l1a_l23api_init(void)
+{
+ sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
+}
diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c
new file mode 100644
index 00000000..e51d7582
--- /dev/null
+++ b/src/target/firmware/layer1/mframe_sched.c
@@ -0,0 +1,370 @@
+/* GSM Multiframe Scheduler Implementation (on top of TDMA sched) */
+
+/* (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 <string.h>
+
+#include <debug.h>
+
+#include <osmocore/gsm_utils.h>
+
+#include <layer1/sync.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+
+/* A multiframe operation which can be scheduled for a multiframe */
+struct mframe_sched_item {
+ /* The TDMA scheduler item that shall be scheduled */
+ const struct tdma_sched_item *sched_set;
+ /* Which modulo shall be used on the frame number */
+ uint16_t modulo;
+ /* At which number inside the modulo shall we be scheduled */
+ uint16_t frame_nr;
+ /* bit-mask of flags */
+ uint16_t flags;
+};
+
+/* FIXME: properly clean this up */
+extern const struct tdma_sched_item nb_sched_set[];
+extern const struct tdma_sched_item nb_sched_set_ul[];
+#define NB_QUAD_DL nb_sched_set
+#define NB_QUAD_FH_DL NB_QUAD_DL
+#define NB_QUAD_UL nb_sched_set_ul
+#define NB_QUAD_FH_UL NB_QUAD_UL
+
+/* BCCH Normal */
+static const struct mframe_sched_item mf_bcch_norm[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 2 },
+ { .sched_set = NULL }
+};
+
+/* BCCH Extended */
+static const struct mframe_sched_item mf_bcch_ext[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
+ { .sched_set = NULL }
+};
+
+/* Full CCCH in a pure BCCH + CCCH C0T0 */
+static const struct mframe_sched_item mf_ccch[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 42 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 46 },
+ { .sched_set = NULL }
+};
+
+/* Full CCCH in a combined CCCH on C0T0 */
+static const struct mframe_sched_item mf_ccch_comb[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 },
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 },
+ { .sched_set = NULL }
+};
+
+/* SDCCH/4 in a combined CCCH on C0T0, cannot be FH */
+static const struct mframe_sched_item mf_sdcch4_0[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 },
+ { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 22+15 },
+ { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 42,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 42+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch4_1[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 },
+ { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 26+15 },
+ { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 46,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 46+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch4_2[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 },
+ { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 32+15 },
+ { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+42,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+42+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch4_3[] = {
+ { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 },
+ { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 36+15 },
+ { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+46,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+46+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+
+/* SDCCH/8, can be frequency hopping (FH) */
+static const struct mframe_sched_item mf_sdcch8_0[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 0 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 0+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 32,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 32+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_1[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 4 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 4+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 36,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 36+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_2[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 8 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 8+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 40,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 40+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_3[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 12 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 12+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 44,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 44+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_4[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 16 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 16+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+32,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+32+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_5[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 20 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 20+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+36,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+36+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_6[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 24 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 24+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+40,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+40+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+static const struct mframe_sched_item mf_sdcch8_7[] = {
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 28 },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 28+15 },
+ { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+44,
+ .flags = MF_F_SACCH },
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+44+15,
+ .flags = MF_F_SACCH },
+ { .sched_set = NULL }
+};
+
+static const struct mframe_sched_item mf_tx_all_nb[] = {
+ { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 },
+ { .sched_set = NULL }
+};
+
+static const struct mframe_sched_item *sched_set_for_task[32] = {
+ [MF_TASK_BCCH_NORM] = mf_bcch_norm,
+ [MF_TASK_BCCH_EXT] = mf_bcch_ext,
+ [MF_TASK_CCCH] = mf_ccch,
+ [MF_TASK_CCCH_COMB] = mf_ccch_comb,
+
+ [MF_TASK_SDCCH4_0] = mf_sdcch4_0,
+ [MF_TASK_SDCCH4_1] = mf_sdcch4_1,
+ [MF_TASK_SDCCH4_2] = mf_sdcch4_2,
+ [MF_TASK_SDCCH4_3] = mf_sdcch4_3,
+
+ [MF_TASK_SDCCH8_0] = mf_sdcch8_0,
+ [MF_TASK_SDCCH8_1] = mf_sdcch8_1,
+ [MF_TASK_SDCCH8_2] = mf_sdcch8_2,
+ [MF_TASK_SDCCH8_3] = mf_sdcch8_3,
+ [MF_TASK_SDCCH8_4] = mf_sdcch8_4,
+ [MF_TASK_SDCCH8_5] = mf_sdcch8_5,
+ [MF_TASK_SDCCH8_6] = mf_sdcch8_6,
+ [MF_TASK_SDCCH8_7] = mf_sdcch8_7,
+
+ [MF_TASK_UL_ALL_NB] = mf_tx_all_nb,
+};
+
+/* encodes a channel number according to 08.58 Chapter 9.3.1 */
+uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts)
+{
+ uint8_t cbits;
+
+ switch (mft) {
+ case MF_TASK_BCCH_NORM:
+ case MF_TASK_BCCH_EXT:
+ cbits = 0x10;
+ break;
+ case MF_TASK_CCCH:
+ case MF_TASK_CCCH_COMB:
+ cbits = 0x12;
+ break;
+ case MF_TASK_SDCCH4_0:
+ cbits = 0x04 + 0;
+ break;
+ case MF_TASK_SDCCH4_1:
+ cbits = 0x04 + 1;
+ break;
+ case MF_TASK_SDCCH4_2:
+ cbits = 0x04 + 2;
+ break;
+ case MF_TASK_SDCCH4_3:
+ cbits = 0x04 + 3;
+ break;
+ case MF_TASK_SDCCH8_0:
+ cbits = 0x08 + 0;
+ break;
+ case MF_TASK_SDCCH8_1:
+ cbits = 0x08 + 1;
+ break;
+ case MF_TASK_SDCCH8_2:
+ cbits = 0x08 + 2;
+ break;
+ case MF_TASK_SDCCH8_3:
+ cbits = 0x08 + 3;
+ break;
+ case MF_TASK_SDCCH8_4:
+ cbits = 0x08 + 4;
+ break;
+ case MF_TASK_SDCCH8_5:
+ cbits = 0x08 + 5;
+ break;
+ case MF_TASK_SDCCH8_6:
+ cbits = 0x08 + 6;
+ break;
+ case MF_TASK_SDCCH8_7:
+ cbits = 0x08 + 7;
+ break;
+ case MF_TASK_UL_ALL_NB:
+ /* ERROR: cannot express as channel number */
+ cbits = 0;
+ break;
+ }
+
+ return (cbits << 3) | (ts & 0x7);
+}
+
+/* how many TDMA frame ticks should we schedule events ahead? */
+#define SCHEDULE_AHEAD 2
+
+/* how long do we need to tell the DSP in advance what we want to do? */
+#define SCHEDULE_LATENCY 1
+
+/* (test and) schedule one particular sched_item_set by means of the TDMA scheduler */
+static void mframe_schedule_set(enum mframe_task task_id)
+{
+ const struct mframe_sched_item *set = sched_set_for_task[task_id];
+ const struct mframe_sched_item *si;
+
+ for (si = set; si->sched_set != NULL; si++) {
+ unsigned int trigger = si->frame_nr % si->modulo;
+ unsigned int current = (l1s.current_time.fn + SCHEDULE_AHEAD) % si->modulo;
+ if (current == trigger) {
+ uint32_t fn;
+ int rv;
+
+ /* Schedule the set */
+ /* FIXME: what to do with SACCH Flag etc? */
+ rv = tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY,
+ si->sched_set, task_id | (si->flags<<8));
+
+ /* Compute the next safe time to queue a DSP command */
+ fn = l1s.current_time.fn;
+ ADD_MODULO(fn, rv - 2, GSM_MAX_FN); /* -2 = worst case last dsp command */
+ if ((fn > l1s.mframe_sched.safe_fn) ||
+ (l1s.mframe_sched.safe_fn >= GSM_MAX_FN))
+ l1s.mframe_sched.safe_fn = fn;
+ }
+ }
+}
+
+/* Enable a specific task */
+void mframe_enable(enum mframe_task task_id)
+{
+ l1s.mframe_sched.tasks_tgt |= (1 << task_id);
+}
+
+/* Disable a specific task */
+void mframe_disable(enum mframe_task task_id)
+{
+ l1s.mframe_sched.tasks_tgt &= ~(1 << task_id);
+}
+
+/* Replace the current active set by the new one */
+void mframe_set(uint32_t tasks)
+{
+ l1s.mframe_sched.tasks_tgt = tasks;
+}
+
+/* Schedule mframe_sched_items according to current MF TASK list */
+void mframe_schedule(void)
+{
+ unsigned int i;
+ int fn_diff;
+
+ /* Try to enable/disable task to meet target bitmap */
+ fn_diff = l1s.mframe_sched.safe_fn - l1s.current_time.fn;
+ if ((fn_diff <= 0) || (fn_diff >= (GSM_MAX_FN>>1)) ||
+ (l1s.mframe_sched.safe_fn >= GSM_MAX_FN))
+ /* If nothing is in the way, enable new tasks */
+ l1s.mframe_sched.tasks = l1s.mframe_sched.tasks_tgt;
+ else
+ /* Else, Disable only */
+ l1s.mframe_sched.tasks &= l1s.mframe_sched.tasks_tgt;
+
+ /* Schedule any active pending set */
+ for (i = 0; i < 32; i++) {
+ if (l1s.mframe_sched.tasks & (1 << i))
+ mframe_schedule_set(i);
+ }
+}
+
+/* reset the scheduler, disabling all tasks */
+void mframe_reset(void)
+{
+ l1s.mframe_sched.tasks = 0;
+ l1s.mframe_sched.tasks_tgt = 0;
+ l1s.mframe_sched.safe_fn = -1UL; /* Force safe */
+}
+
diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c
new file mode 100644
index 00000000..4360f7c4
--- /dev/null
+++ b/src/target/firmware/layer1/prim_fbsb.c
@@ -0,0 +1,572 @@
+/* Layer 1 - FCCH and SCH burst handling */
+
+/* (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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/afc.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+
+#include <l1a_l23_interface.h>
+
+#define FB0_RETRY_COUNT 3
+#define AFC_RETRY_COUNT 30
+
+extern uint16_t rf_arfcn; // TODO
+
+struct mon_state {
+ uint32_t fnr_report; /* frame number when DSP reported it */
+ int attempt; /* which attempt was this ? */
+
+ int16_t toa;
+ uint16_t pm;
+ uint16_t angle;
+ uint16_t snr;
+
+ /* computed values */
+ int16_t freq_diff;
+
+ /* Sync Burst (SB) */
+ uint8_t bsic;
+ struct gsm_time time;
+};
+
+struct l1a_fb_state {
+ struct mon_state mon;
+ struct l1ctl_fbsb_req req;
+ int16_t initial_freq_err;
+ uint8_t fb_retries;
+ uint8_t afc_retries;
+};
+
+static struct l1a_fb_state fbs;
+static struct mon_state *last_fb = &fbs.mon;
+
+static void dump_mon_state(struct mon_state *fb)
+{
+#if 0
+ printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, "
+ "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n",
+ fb->fnr_report, fb->attempt, fb->toa,
+ agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle),
+ fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr),
+ tpu_get_offset(), tpu_get_synchro());
+#else
+ printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n",
+ fb->fnr_report, fb->attempt, fb->toa,
+ agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle));
+#endif
+}
+
+static int l1ctl_fbsb_resp(uint8_t res)
+{
+ struct msgb *msg;
+ struct l1ctl_fbsb_conf *resp;
+
+ msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn,
+ l1s_snr_int(fbs.mon.snr),
+ fbs.req.band_arfcn);
+ if (!msg)
+ return -ENOMEM;
+
+ resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp));
+ resp->initial_freq_err = htons(fbs.initial_freq_err);
+ resp->result = res;
+ resp->bsic = fbs.mon.bsic;
+
+ /* no need to set BSIC, as it is never used here */
+ l1_queue_for_l2(msg);
+
+ return 0;
+}
+
+/* SCH Burst Detection ********************************************************/
+
+/* determine the GSM time and BSIC from a Sync Burst */
+static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb)
+{
+ uint8_t bsic = (sb >> 2) & 0x3f;
+ uint8_t t3p;
+
+ memset(time, 0, sizeof(*time));
+
+ /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */
+ time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600);
+ time->t2 = (sb >> 18) & 0x1f;
+ t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6);
+ time->t3 = t3p*10 + 1;
+
+ /* TS 05.02 Chapter 4.3.3 TDMA frame number */
+ time->fn = gsm_gsmtime2fn(time);
+
+ time->tc = (time->fn / 51) % 8;
+
+ return bsic;
+}
+
+static void read_sb_result(struct mon_state *st, int attempt)
+{
+ st->toa = dsp_api.db_r->a_serv_demod[D_TOA];
+ st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3;
+ st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE];
+ st->snr = dsp_api.db_r->a_serv_demod[D_SNR];
+
+ st->freq_diff = ANGLE_TO_FREQ(st->angle);
+ st->fnr_report = l1s.current_time.fn;
+ st->attempt = attempt;
+
+ dump_mon_state(st);
+
+ if (st->snr > AFC_SNR_THRESHOLD)
+ afc_input(st->freq_diff, rf_arfcn, 1);
+ else
+ afc_input(st->freq_diff, rf_arfcn, 0);
+
+ dsp_api.r_page_used = 1;
+}
+
+/* Note: When we get the SB response, it is 2 TDMA frames after the SB
+ * actually happened, as it is a "C W W R" task */
+#define SB2_LATENCY 2
+
+static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt,
+ __unused uint16_t p3)
+{
+ uint32_t sb;
+ int qbits, fn_offset;
+ struct l1_cell_info *cinfo = &l1s.serving_cell;
+ int fnr_delta, bits_delta;
+ struct l1ctl_sync_new_ccch_resp *l1;
+ struct msgb *msg;
+
+ putchart('s');
+
+ if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) {
+ /* mark READ page as being used */
+ dsp_api.r_page_used = 1;
+
+ /* after 2nd attempt, we failed */
+ if (attempt == 2) {
+ last_fb->attempt = 13;
+ l1s_compl_sched(L1_COMPL_FB);
+ }
+
+ /* after 1st attempt, we simply wait for 2nd */
+ return 0;
+ }
+
+ printf("SB%d ", attempt);
+ read_sb_result(last_fb, attempt);
+
+ sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16;
+ fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb);
+ printf("=> SB 0x%08x: BSIC=%u ", sb, fbs.mon.bsic);
+ l1s_time_dump(&fbs.mon.time);
+
+ l1s.serving_cell.bsic = fbs.mon.bsic;
+
+ /* calculate synchronisation value (TODO: only complete for qbits) */
+ last_fb->toa -= 23;
+ qbits = last_fb->toa * 4;
+ fn_offset = l1s.current_time.fn; // TODO
+
+ if (qbits > QBITS_PER_TDMA) {
+ qbits -= QBITS_PER_TDMA;
+ fn_offset -= 1;
+ } else if (qbits < 0) {
+ qbits += QBITS_PER_TDMA;
+ fn_offset += 1;
+ }
+
+ fnr_delta = last_fb->fnr_report - attempt;
+ bits_delta = fnr_delta * BITS_PER_TDMA;
+
+ cinfo->fn_offset = fnr_delta;
+ cinfo->time_alignment = qbits;
+ cinfo->arfcn = rf_arfcn;
+
+ if (last_fb->toa > bits_delta)
+ printf("=> DSP reports SB in bit that is %d bits in the "
+ "future?!?\n", last_fb->toa - bits_delta);
+ else
+ printf(" qbits=%u\n", qbits);
+
+ synchronize_tdma(&l1s.serving_cell);
+
+ /* if we have recived a SYNC burst, update our local GSM time */
+ gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY);
+ /* compute next time from new current time */
+ l1s.next_time = l1s.current_time;
+ l1s_time_inc(&l1s.next_time, 1);
+
+ /* If we call tdma_sched_reset(), which is only needed if there
+ * are further l1s_sbdet_resp() scheduled, we will bring
+ * dsp_api.db_r and dsp_api.db_w out of sync because we changed
+ * dsp_api.db_w for l1s_sbdet_cmd() and canceled
+ * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP
+ * however expects dsp_api.db_w and dsp_api.db_r to be in sync
+ * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w
+ * and dsp_api.db_r into sync again, otherwise NB reading will
+ * complain. We probably don't need the Abort command and could
+ * just bring dsp_api.db_w and dsp_api.db_r into sync. */
+ if (attempt != 2) {
+ tdma_sched_reset();
+ l1s_dsp_abort();
+ }
+
+ l1s_reset_hw();
+ /* enable the MF Task for BCCH reading */
+ mframe_enable(MF_TASK_BCCH_NORM);
+ if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED)
+ mframe_enable(MF_TASK_CCCH_COMB);
+ else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED)
+ mframe_enable(MF_TASK_CCCH);
+
+ l1s_compl_sched(L1_COMPL_FB);
+
+ return 0;
+}
+
+static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ putchart('S');
+
+ fbs.mon.bsic = 0;
+ fbs.mon.time.fn = 0;
+
+ dsp_api.db_w->d_task_md = SB_DSP_TASK;
+ dsp_api.ndb->d_fb_mode = 0; /* wideband search */
+ dsp_end_scenario();
+
+ /* Program TPU */
+ l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0);
+ tpu_end_scenario();
+
+ return 0;
+}
+
+/* This is how it is done by the TSM30 */
+static const struct tdma_sched_item sb_sched_set[] = {
+ SCHED_ITEM(l1s_sbdet_cmd, 0, 1), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_sbdet_cmd, 0, 2), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_sbdet_resp, 0, 1), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_sbdet_resp, 0, 2), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+void l1s_sb_test(uint8_t base_fn)
+{
+ tdma_schedule_set(base_fn, sb_sched_set, 0);
+}
+/* FCCH Burst *****************************************************************/
+
+static int read_fb_result(struct mon_state *st, int attempt)
+{
+ st->toa = dsp_api.ndb->a_sync_demod[D_TOA];
+ st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3;
+ st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE];
+ st->snr = dsp_api.ndb->a_sync_demod[D_SNR];
+
+ //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE);
+ st->freq_diff = ANGLE_TO_FREQ(last_fb->angle);
+ st->fnr_report = l1s.current_time.fn;
+ st->attempt = attempt;
+
+ dump_mon_state(st);
+
+ dsp_api.ndb->d_fb_det = 0;
+ dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */
+
+ /* Update AFC with current frequency offset */
+ afc_correct(st->freq_diff, rf_arfcn);
+
+ //tpu_dsp_frameirq_enable();
+ return 1;
+}
+
+static void fbinfo2cellinfo(struct l1_cell_info *cinfo,
+ const struct mon_state *mon)
+{
+ int ntdma, qbits, fn_offset, fnr_delta, bits_delta;
+
+ /* FIXME: where did this magic 23 come from? */
+ last_fb->toa -= 23;
+
+ if (last_fb->toa < 0) {
+ qbits = (last_fb->toa + BITS_PER_TDMA) * 4;
+ ntdma = -1;
+ } else {
+ ntdma = (last_fb->toa) / BITS_PER_TDMA;
+ qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4;
+ }
+
+ fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma;
+ fnr_delta = last_fb->fnr_report - last_fb->attempt;
+ bits_delta = fnr_delta * BITS_PER_TDMA;
+
+ cinfo->fn_offset = fnr_delta;
+ cinfo->time_alignment = qbits;
+ cinfo->arfcn = rf_arfcn;
+
+ if (last_fb->toa > bits_delta)
+ printf("=> DSP reports FB in bit that is %d bits in "
+ "the future?!?\n", last_fb->toa - bits_delta);
+ else {
+ int fb_fnr = (last_fb->fnr_report - last_fb->attempt)
+ + last_fb->toa/BITS_PER_TDMA;
+ printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n",
+ fb_fnr, fn_offset, qbits);
+ }
+}
+
+/* scheduler callback to issue a FB detection task to the DSP */
+static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ uint16_t fb_mode)
+{
+ if (fb_mode == 0) {
+ putchart('F');
+ } else {
+ putchart('V');
+ }
+
+ l1s.fb.mode = fb_mode;
+
+ /* Tell the RF frontend to set the gain appropriately */
+ rffe_set_gain(-85, CAL_DSP_TGT_BB_LVL);
+
+ /* Program DSP */
+ dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */
+ dsp_api.ndb->d_fb_mode = fb_mode;
+ dsp_end_scenario();
+
+ /* Program TPU */
+ l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0);
+ tpu_end_scenario();
+
+ return 0;
+}
+
+#if 0
+#define FB0_SNR_THRESH 2000
+#define FB1_SNR_THRESH 3000
+#else
+#define FB0_SNR_THRESH 0
+#define FB1_SNR_THRESH 0
+#endif
+
+static const struct tdma_sched_item fb_sched_set[];
+
+/* scheduler callback to check for a FB detection response */
+static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt,
+ uint16_t fb_mode)
+{
+ putchart('f');
+
+ if (!dsp_api.ndb->d_fb_det) {
+ /* we did not detect a FB */
+
+ /* attempt < 12, do nothing */
+ if (attempt < 12)
+ return 0;
+
+ /* attempt >= 12, we simply don't find one */
+
+ /* If we don't reset here, we get DSP DMA errors */
+ tdma_sched_reset();
+
+ if (fbs.fb_retries < FB0_RETRY_COUNT) {
+ /* retry once more */
+ tdma_schedule_set(1, fb_sched_set, 0);
+ fbs.fb_retries++;
+ } else {
+ last_fb->attempt = 13;
+ l1s_compl_sched(L1_COMPL_FB);
+ }
+
+ return 0;
+ }
+
+ /* We found a frequency burst, reset everything */
+ l1s_reset_hw();
+
+ printf("FB%u ", dsp_api.ndb->d_fb_mode);
+ read_fb_result(last_fb, attempt);
+
+ /* if this is the first success, save freq err */
+ if (!fbs.initial_freq_err)
+ fbs.initial_freq_err = last_fb->freq_diff;
+
+ /* If we don't reset here, we get DSP DMA errors */
+ tdma_sched_reset();
+
+ /* Immediately schedule further TDMA tasklets, if requested. Doing
+ * this directly from L1S means we can do this quickly without any
+ * additional delays */
+ if (fb_mode == 0) {
+ if (fbs.req.flags & L1CTL_FBSB_F_FB1) {
+ /* If we don't reset here, we get DSP DMA errors */
+ tdma_sched_reset();
+ /* FIXME: don't only use the last but an average */
+ if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 &&
+ last_fb->snr > FB0_SNR_THRESH) {
+ /* continue with FB1 task in DSP */
+ tdma_schedule_set(1, fb_sched_set, 1);
+ } else {
+ if (fbs.afc_retries < AFC_RETRY_COUNT) {
+ tdma_schedule_set(1, fb_sched_set, 0);
+ fbs.afc_retries++;
+ } else {
+ /* Abort */
+ last_fb->attempt = 13;
+ l1s_compl_sched(L1_COMPL_FB);
+ }
+ }
+ } else
+ l1s_compl_sched(L1_COMPL_FB);
+ } else if (fb_mode == 1) {
+ if (fbs.req.flags & L1CTL_FBSB_F_SB) {
+
+ int ntdma, qbits;
+ /* FIXME: where did this magic 23 come from? */
+ last_fb->toa -= 23;
+
+ if (last_fb->toa < 0) {
+ qbits = (last_fb->toa + BITS_PER_TDMA) * 4;
+ ntdma = -1;
+ } else {
+ ntdma = (last_fb->toa) / BITS_PER_TDMA;
+ qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4;
+ }
+
+
+ int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma;
+ int delay = fn_offset + 11 - l1s.current_time.fn - 1;
+ printf(" fn_offset=%d (fn=%u + attempt=%u + ntdma = %d)\m",
+ fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma);
+ printf(" delay=%d (fn_offset=%d + 11 - fn=%u - 1\n", delay,
+ fn_offset, l1s.current_time.fn);
+ printf(" scheduling next FB/SB detection task with delay %u\n", delay);
+ if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 &&
+ last_fb->snr > FB1_SNR_THRESH) {
+ /* synchronize before reading SB */
+ fbinfo2cellinfo(&l1s.serving_cell, last_fb);
+ synchronize_tdma(&l1s.serving_cell);
+ tdma_schedule_set(delay, sb_sched_set, 0);
+ } else
+ tdma_schedule_set(delay, fb_sched_set, 1);
+ } else
+ l1s_compl_sched(L1_COMPL_FB);
+ }
+
+ return 0;
+}
+
+/* FB detection */
+static const struct tdma_sched_item fb_sched_set[] = {
+ SCHED_ITEM(l1s_fbdet_cmd, 0, 0), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 1), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 2), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 3), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 4), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 5), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 6), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 7), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 8), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 9), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 10), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 11), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_fbdet_resp, 0, 12), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+/* Asynchronous completion handler for FB detection */
+static void l1a_fb_compl(__unused enum l1_compl c)
+{
+ struct l1_cell_info *cinfo = &l1s.serving_cell;
+
+ if (last_fb->attempt >= 13) {
+ /* FB detection failed, signal this via L1CTL */
+ return l1ctl_fbsb_resp(255);
+ }
+
+ /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */
+ fbinfo2cellinfo(&l1s.serving_cell, last_fb);
+
+ /* send FBSB_CONF success message via L1CTL */
+ l1ctl_fbsb_resp(0);
+}
+
+void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req)
+{
+ /* copy + endian convert request data */
+ fbs.req.band_arfcn = ntohs(req->band_arfcn);
+ fbs.req.timeout = ntohs(req->timeout);
+ fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1);
+ fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2);
+ fbs.req.num_freqerr_avg = req->num_freqerr_avg;
+ fbs.req.flags = req->flags;
+ fbs.req.sync_info_idx = req->sync_info_idx;
+
+ /* clear initial frequency error */
+ fbs.initial_freq_err = 0;
+ fbs.fb_retries = 0;
+
+ /* Make sure we start at a 'center' AFCDAC output value */
+ afc_reset();
+
+ if (fbs.req.flags & L1CTL_FBSB_F_FB0)
+ tdma_schedule_set(base_fn, fb_sched_set, 0);
+ else if (fbs.req.flags & L1CTL_FBSB_F_FB1)
+ tdma_schedule_set(base_fn, fb_sched_set, 0);
+ else if (fbs.req.flags & L1CTL_FBSB_F_SB)
+ tdma_schedule_set(base_fn, sb_sched_set, 0);
+
+}
+
+static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void)
+{
+ l1s.completion[L1_COMPL_FB] = &l1a_fb_compl;
+}
diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c
new file mode 100644
index 00000000..80650f60
--- /dev/null
+++ b/src/target/firmware/layer1/prim_pm.c
@@ -0,0 +1,148 @@
+/* Layer 1 Power Measurement */
+
+/* (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 <string.h>
+#include <stdlib.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/agc.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+
+#include <l1a_l23_interface.h>
+
+static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm)
+{
+ uint8_t i;
+
+ for (i = 0; i < nbmeas; i++)
+ pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3);
+ dsp_api.r_page_used = 1;
+}
+
+/* scheduler callback to issue a power measurement task to the DSP */
+static int l1s_pm_cmd(uint8_t num_meas,
+ __unused uint8_t p2, uint16_t arfcn)
+{
+ putchart('P');
+
+ dsp_api.db_w->d_task_md = num_meas; /* number of measurements */
+ dsp_api.ndb->d_fb_mode = 0; /* wideband search */
+ dsp_end_scenario();
+
+ /* Tell the RF frontend to set the gain appropriately */
+ rffe_set_gain(-85, CAL_DSP_TGT_BB_LVL);
+
+ /* Program TPU */
+ /* FIXME: RXWIN_PW needs to set up multiple times in case
+ * num_meas > 1 */
+ l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW, 0);
+ //l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB);
+ tpu_end_scenario();
+
+ return 0;
+}
+
+/* scheduler callback to read power measurement resposnse from the DSP */
+static int l1s_pm_resp(uint8_t num_meas, __unused uint8_t p2,
+ uint16_t arfcn)
+{
+ struct l1ctl_pm_conf *pmr;
+ uint16_t pm_level[2];
+
+ putchart('p');
+
+ l1ddsp_meas_read(num_meas, pm_level);
+
+ printf("PM MEAS: ARFCN=%u, %-4d dBm at baseband, %-4d dBm at RF\n",
+ arfcn, pm_level[0]/8, agc_inp_dbm8_by_pm(pm_level[0])/8);
+
+ printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n",
+ agc_inp_dbm8_by_pm(pm_level[0])/8,
+ agc_inp_dbm8_by_pm(pm_level[1])/8, arfcn);
+
+ if (!l1s.pm.msg)
+ l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
+
+ if (msgb_tailroom(l1s.pm.msg) < sizeof(*pmr)) {
+ /* flush current msgb */
+ l1_queue_for_l2(l1s.pm.msg);
+ /* allocate a new msgb and initialize header */
+ l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
+ }
+
+ pmr = msgb_put(l1s.pm.msg, sizeof(*pmr));
+ pmr->band_arfcn = htons(arfcn);
+ /* FIXME: do this as RxLev rather than DBM8 ? */
+ pmr->pm[0] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[0])/8);
+ if (num_meas > 1)
+ pmr->pm[1] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[1])/8);
+ else
+ pmr->pm[1] = 0;
+
+ if (l1s.pm.mode == 1) {
+ if (l1s.pm.range.arfcn_next <= l1s.pm.range.arfcn_end) {
+ /* schedule PM for next ARFCN in range */
+ l1s_pm_test(1, l1s.pm.range.arfcn_next);
+ l1s.pm.range.arfcn_next++;
+ } else {
+ /* we have finished, flush the msgb to L2 */
+ struct l1ctl_hdr *l1h = l1s.pm.msg->l1h;
+ l1h->flags |= L1CTL_F_DONE;
+ l1_queue_for_l2(l1s.pm.msg);
+ l1s.pm.msg = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static const struct tdma_sched_item pm_sched_set[] = {
+ SCHED_ITEM(l1s_pm_cmd, 1, 0), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_pm_resp, 1, 0), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+/* Schedule a power measurement test */
+void l1s_pm_test(uint8_t base_fn, uint16_t arfcn)
+{
+ printd("l1s_pm_test(%u, %u)\n", base_fn, arfcn);
+ tdma_schedule_set(base_fn, pm_sched_set, arfcn);
+}
diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c
new file mode 100644
index 00000000..f91af3dd
--- /dev/null
+++ b/src/target/firmware/layer1/prim_rach.c
@@ -0,0 +1,136 @@
+/* Layer 1 Random Access Channel Burst */
+
+/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de>
+ * (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 <string.h>
+#include <stdlib.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/async.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+
+#include <l1a_l23_interface.h>
+
+struct {
+ uint32_t fn;
+ uint16_t band_arfcn;
+} last_rach;
+
+/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */
+static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3)
+{
+ int i;
+ uint16_t *info_ptr;
+ uint8_t data[2];
+
+ putchart('T');
+
+ l1s_tx_apc_helper();
+
+ data[0] = l1s.serving_cell.bsic << 2;
+ data[1] = l1s.rach.ra;
+
+ info_ptr = &dsp_api.ndb->d_rach;
+ info_ptr[0] = ((uint16_t)(data[0])) | ((uint16_t)(data[1])<<8);
+
+ dsp_api.db_w->d_task_ra = RACH_DSP_TASK;
+ dsp_end_scenario();
+
+ l1s_tx_win_ctrl(l1s.serving_cell.arfcn, L1_TXWIN_AB, 0, 0);
+ tpu_end_scenario();
+
+ return 0;
+}
+
+/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */
+static int l1s_tx_rach_resp(__unused uint8_t p1, __unused uint8_t burst_id,
+ __unused uint16_t p3)
+{
+ putchart('t');
+
+ dsp_api.r_page_used = 1;
+
+ /* schedule a confirmation back indicating the GSM time at which
+ * the RACH burst was transmitted to the BTS */
+ last_rach.fn = l1s.current_time.fn - 1;
+ last_rach.band_arfcn = l1s.serving_cell.arfcn;
+ l1s_compl_sched(L1_COMPL_RACH);
+
+ return 0;
+}
+
+/* sched sets for uplink */
+const struct tdma_sched_item rach_sched_set_ul[] = {
+ SCHED_ITEM(l1s_tx_rach_cmd, 1, 0), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_tx_rach_resp, 1, 0), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+/* Asynchronous completion handler for FB detection */
+static void l1a_rach_compl(__unused enum l1_compl c)
+{
+ struct msgb *msg;
+
+ msg = l1_create_l2_msg(L1CTL_RACH_CONF, last_rach.fn, 0,
+ last_rach.band_arfcn);
+ l1_queue_for_l2(msg);
+}
+
+/* request a RACH request at the next multiframe T3 = fn51 */
+void l1a_rach_req(uint8_t fn51, uint8_t ra)
+{
+ uint32_t fn_sched;
+
+ l1a_lock_sync();
+ l1s.rach.ra = ra;
+ /* TODO: can we wrap here? I don't think so */
+ fn_sched = l1s.current_time.fn - l1s.current_time.t3;
+ fn_sched += fn51;
+ sched_gsmtime(rach_sched_set_ul, fn_sched, 0);
+ l1a_unlock_sync();
+
+ memset(&last_rach, 0, sizeof(last_rach));
+}
+
+static __attribute__ ((constructor)) void prim_rach_init(void)
+{
+ l1s.completion[L1_COMPL_RACH] = &l1a_rach_compl;
+}
diff --git a/src/target/firmware/layer1/prim_rx_nb.c b/src/target/firmware/layer1/prim_rx_nb.c
new file mode 100644
index 00000000..036f4e6d
--- /dev/null
+++ b/src/target/firmware/layer1/prim_rx_nb.c
@@ -0,0 +1,199 @@
+/* Layer 1 - Receiving Normal Bursts */
+
+/* (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 <string.h>
+#include <stdlib.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/afc.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+#include <layer1/rfch.h>
+
+#include <l1a_l23_interface.h>
+
+struct l1s_rxnb_state {
+ struct l1s_meas_hdr meas[4];
+
+ struct msgb *msg;
+ struct l1ctl_info_dl *dl;
+ struct l1ctl_data_ind *di;
+};
+
+static struct l1s_rxnb_state rxnb;
+
+static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3)
+{
+ struct gsm_time rx_time;
+ uint8_t mf_task_id = p3 & 0xff;
+ uint8_t mf_task_flags = p3 >> 8;
+ uint16_t rf_arfcn;
+ uint8_t tsc, tn;
+
+ putchart('n');
+
+ gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 4);
+ rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn);
+
+ /* just for debugging, d_task_d should not be 0 */
+ if (dsp_api.db_r->d_task_d == 0) {
+ puts("EMPTY\n");
+ return 0;
+ }
+
+ /* DSP burst ID needs to corespond with what we expect */
+ if (dsp_api.db_r->d_burst_d != burst_id) {
+ printf("BURST ID %u!=%u\n", dsp_api.db_r->d_burst_d, burst_id);
+ return 0;
+ }
+
+ rxnb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA];
+ rxnb.meas[burst_id].pm_dbm8 =
+ agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3);
+ rxnb.meas[burst_id].freq_err =
+ ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]);
+ rxnb.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR];
+
+ /* feed computed frequency error into AFC loop */
+ if (rxnb.meas[burst_id].snr > AFC_SNR_THRESHOLD)
+ afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 1);
+ else
+ afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 0);
+
+ /* Tell the RF frontend to set the gain appropriately */
+ rffe_set_gain(rxnb.meas[burst_id].pm_dbm8/8, CAL_DSP_TGT_BB_LVL);
+
+ /* 4th burst, get frame data */
+ if (dsp_api.db_r->d_burst_d == 3) {
+ uint8_t i, j;
+ uint16_t num_biterr;
+ uint32_t avg_snr = 0;
+ int32_t avg_dbm8 = 0;
+
+ /* Set Channel Number depending on MFrame Task ID */
+ rxnb.dl->chan_nr = mframe_task2chan_nr(mf_task_id, tn);
+
+ /* Set SACCH indication in Link IDentifier */
+ if (mf_task_flags & MF_F_SACCH)
+ rxnb.dl->link_id = 0x40;
+ else
+ rxnb.dl->link_id = 0x00;
+
+ rxnb.dl->band_arfcn = htons(rf_arfcn);
+
+ rxnb.dl->frame_nr = htonl(l1s.current_time.fn-4);
+
+ /* compute average snr and rx level */
+ for (i = 0; i < 4; ++i) {
+ avg_snr += rxnb.meas[i].snr;
+ avg_dbm8 += rxnb.meas[i].pm_dbm8;
+ }
+ rxnb.dl->snr = avg_snr / 4;
+ rxnb.dl->rx_level = (avg_dbm8 / (8*4)) + 110;
+
+ num_biterr = dsp_api.ndb->a_cd[2] & 0xffff;
+ if (num_biterr > 0xff)
+ rxnb.dl->num_biterr = 0xff;
+ else
+ rxnb.dl->num_biterr = num_biterr;
+
+ rxnb.dl->fire_crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0;
+
+ /* copy actual data, skipping the information block [0,1,2] */
+ for (j = 0,i = 3; i < 15; i++) {
+ rxnb.di->data[j++] = dsp_api.ndb->a_cd[i] & 0xFF;
+ rxnb.di->data[j++] = (dsp_api.ndb->a_cd[i] >> 8) & 0xFF;
+ }
+
+ l1_queue_for_l2(rxnb.msg);
+ rxnb.msg = NULL; rxnb.dl = NULL; rxnb.di = NULL;
+
+ /* clear downlink task */
+ dsp_api.db_w->d_task_d = 0;
+ }
+
+ /* mark READ page as being used */
+ dsp_api.r_page_used = 1;
+
+ return 0;
+}
+
+static int l1s_nb_cmd(__unused uint8_t p1, uint8_t burst_id,
+ __unused uint16_t p3)
+{
+ uint16_t arfcn;
+ uint8_t tsc, tn;
+
+ putchart('N');
+
+ if (burst_id == 1) {
+ /* allocate message only at 2nd burst in case of
+ * consecutive/overlapping normal burst RX tasks */
+ /* FIXME: we actually want all allocation out of L1S! */
+ if (rxnb.msg)
+ printf("nb_cmd(0) and rxnb.msg != NULL\n");
+ /* allocate msgb as needed. FIXME: from L1A ?? */
+ rxnb.msg = l1ctl_msgb_alloc(L1CTL_DATA_IND);
+ if (!rxnb.msg)
+ printf("nb_cmd(0): unable to allocate msgb\n");
+ rxnb.dl = (struct l1ctl_info_dl *) msgb_put(rxnb.msg, sizeof(*rxnb.dl));
+ rxnb.di = (struct l1ctl_data_ind *) msgb_put(rxnb.msg, sizeof(*rxnb.di));
+ }
+
+ rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn);
+
+ dsp_load_rx_task(ALLC_DSP_TASK, burst_id, tsc);
+ dsp_end_scenario();
+
+ l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, tn);
+ tpu_end_scenario();
+
+ return 0;
+}
+
+const struct tdma_sched_item nb_sched_set[] = {
+ SCHED_ITEM(l1s_nb_cmd, 0, 0), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_nb_cmd, 0, 1), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_nb_resp, 0, 0), SCHED_ITEM(l1s_nb_cmd, 0, 2), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_nb_resp, 0, 1), SCHED_ITEM(l1s_nb_cmd, 0, 3), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_nb_resp, 0, 2), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_nb_resp, 0, 3), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
diff --git a/src/target/firmware/layer1/prim_tx_nb.c b/src/target/firmware/layer1/prim_tx_nb.c
new file mode 100644
index 00000000..7e4bd960
--- /dev/null
+++ b/src/target/firmware/layer1/prim_tx_nb.c
@@ -0,0 +1,209 @@
+/* Layer 1 - Transmit Normal Burst */
+
+/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de>
+ * (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 <string.h>
+#include <stdlib.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <osmocore/gsm_utils.h>
+#include <osmocore/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <layer1/sync.h>
+#include <layer1/agc.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+#include <layer1/rfch.h>
+
+#include <l1a_l23_interface.h>
+
+/* Channel type definitions for DEDICATED mode */
+#define INVALID_CHANNEL 0
+#define TCH_F 1
+#define TCH_H 2
+#define SDCCH_4 3
+#define SDCCH_8 4
+
+/* Channel mode definitions for DEDICATED mode */
+#define SIG_ONLY_MODE 0 // signalling only
+#define TCH_FS_MODE 1 // speech full rate
+#define TCH_HS_MODE 2 // speech half rate
+#define TCH_96_MODE 3 // data 9,6 kb/s
+#define TCH_48F_MODE 4 // data 4,8 kb/s full rate
+#define TCH_48H_MODE 5 // data 4,8 kb/s half rate
+#define TCH_24F_MODE 6 // data 2,4 kb/s full rate
+#define TCH_24H_MODE 7 // data 2,4 kb/s half rate
+#define TCH_EFR_MODE 8 // enhanced full rate
+#define TCH_144_MODE 9 // data 14,4 kb/s half rate
+
+static uint32_t last_txnb_fn;
+
+static const uint8_t ubUui[23] = { 0x01, 0x03, 0x01, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b };
+
+/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */
+static int l1s_tx_resp(__unused uint8_t p1, __unused uint8_t burst_id,
+ __unused uint16_t p3)
+{
+ putchart('t');
+
+ dsp_api.r_page_used = 1;
+
+ if (burst_id == 3) {
+ last_txnb_fn = l1s.current_time.fn - 4;
+ l1s_compl_sched(L1_COMPL_TX_NB);
+ }
+
+ return 0;
+}
+
+/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */
+static int l1s_tx_cmd(uint8_t p1, uint8_t burst_id, uint16_t p3)
+{
+ uint16_t arfcn;
+ uint8_t tsc, tn;
+ uint8_t mf_task_id = p3 & 0xff;
+ uint8_t mf_task_flags = p3 >> 8;
+
+ putchart('T');
+
+ l1s_tx_apc_helper();
+
+ if (p1 == 0) /* DUL_DSP_TASK, one normal burst */
+ dsp_load_tch_param(0, SIG_ONLY_MODE, INVALID_CHANNEL, 0, 0, 0);
+ else if (p1 == 2) /* DUL_DSP_TASK, four normal bursts */
+ dsp_load_tch_param(0, SIG_ONLY_MODE, SDCCH_4, 0, 0, 0);
+
+ /* before sending first of the four bursts, copy data to API ram */
+ if (burst_id == 0) {
+ uint16_t *info_ptr = dsp_api.ndb->a_cu;
+ struct llist_head *tx_queue;
+ struct msgb *msg;
+ const uint8_t *data;
+ int i;
+ uint8_t j;
+
+ /* distinguish between DCCH and ACCH */
+ if (mf_task_flags & MF_F_SACCH) {
+ puts("SACCH queue ");
+ tx_queue = &l1s.tx_queue[L1S_CHAN_SACCH];
+ } else {
+ puts("SDCCH queue ");
+ tx_queue = &l1s.tx_queue[L1S_CHAN_MAIN];
+ }
+ msg = msgb_dequeue(tx_queue);
+
+ /* If the TX queue is empty, send idle pattern */
+ if (!msg) {
+ puts("TX idle pattern\n");
+ data = ubUui;
+ } else {
+ puts("TX uplink msg\n");
+ data = msg->l3h;
+ }
+
+ /* Fill data block Header */
+ info_ptr[0] = (1 << B_BLUD); // 1st word: Set B_BLU bit.
+ info_ptr[1] = 0; // 2nd word: cleared.
+ info_ptr[2] = 0; // 3rd word: cleared.
+
+ /* Copy first 22 bytes in the first 11 words after header. */
+ for (i=0, j=(3+0); j<(3+11); j++) {
+ info_ptr[j] = ((uint16_t)(data[i])) | ((uint16_t)(data[i+1]) << 8);
+ printf("%02x %02x ", data[i], data[i+1]);
+ i += 2;
+ }
+ /* Copy last UWORD8 (23rd) in the 12th word after header. */
+ info_ptr[14] = data[22];
+ printf("%02x\n", data[22]);
+
+ if (msg)
+ msgb_free(msg);
+ }
+
+ rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn);
+
+ dsp_load_tx_task(DUL_DSP_TASK, burst_id, tsc);
+ dsp_end_scenario();
+
+ l1s_tx_win_ctrl(arfcn, L1_TXWIN_NB, 0, tn);
+ tpu_end_scenario();
+
+ return 0;
+}
+
+/* Asynchronous completion handler for NB transmit */
+static void l1a_tx_nb_compl(__unused enum l1_compl c)
+{
+ struct msgb *msg;
+
+ msg = l1_create_l2_msg(L1CTL_DATA_CONF, last_txnb_fn, 0, 0);
+ l1_queue_for_l2(msg);
+}
+
+void l1s_tx_test(uint8_t base_fn, uint8_t type)
+{
+ printf("Starting TX %d\n", type);
+
+ if (type == 0) {// one normal burst
+ tdma_schedule(base_fn, &l1s_tx_cmd, 0, 0, 0);
+ tdma_schedule(base_fn + 2, &l1s_tx_resp, 0, 0, 0);
+ } else if (type == 2) { // four normal burst
+ tdma_schedule(base_fn, &l1s_tx_cmd, 2, 0, 0);
+ tdma_schedule(base_fn + 1, &l1s_tx_cmd, 2, 1, 0);
+ tdma_schedule(base_fn + 2, &l1s_tx_resp, 2, 0, 0);
+ tdma_schedule(base_fn + 2, &l1s_tx_cmd, 2, 2, 0);
+ tdma_schedule(base_fn + 3, &l1s_tx_resp, 2, 1, 0);
+ tdma_schedule(base_fn + 3, &l1s_tx_cmd, 2, 3, 0);
+ tdma_schedule(base_fn + 4, &l1s_tx_resp, 2, 2, 0);
+ tdma_schedule(base_fn + 5, &l1s_tx_resp, 2, 3, 0);
+ }
+}
+
+/* sched sets for uplink */
+const struct tdma_sched_item nb_sched_set_ul[] = {
+ SCHED_ITEM(l1s_tx_cmd, 2, 0), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_tx_cmd, 2, 1), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_tx_resp, 2, 0), SCHED_ITEM(l1s_tx_cmd, 2, 2), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_tx_resp, 2, 1), SCHED_ITEM(l1s_tx_cmd, 2, 3), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_tx_resp, 2, 2), SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_tx_resp, 2, 3), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+static __attribute__ ((constructor)) void prim_tx_nb_init(void)
+{
+ l1s.completion[L1_COMPL_TX_NB] = &l1a_tx_nb_compl;
+}
diff --git a/src/target/firmware/layer1/rfch.c b/src/target/firmware/layer1/rfch.c
new file mode 100644
index 00000000..50331bf5
--- /dev/null
+++ b/src/target/firmware/layer1/rfch.c
@@ -0,0 +1,142 @@
+/* RF Channel utilities */
+
+/* (C) 2010 by Sylvain Munaut <tnt@246tNt.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 <stdint.h>
+
+#include <osmocore/gsm_utils.h>
+
+#include <layer1/sync.h>
+
+
+/*
+ * Hopping sequence generation
+ *
+ * The algorithm is explained in GSM 05.02 Section 6.2.3
+ *
+ * if HSN = 0 (cyclic hopping) then:
+ * MAI, integer (0 .. N-1) :
+ * MAI = (FN + MAIO) modulo N
+ *
+ * else:
+ * M, integer (0 .. 152) :
+ * M = T2 + RNTABLE((HSN xor T1R) + T3)
+ *
+ * S, integer (0 .. N-1) :
+ * M' = M modulo (2 ^ NBIN)
+ * T' = T3 modulo (2 ^ NBIN)
+ *
+ * if M' < N then:
+ * S = M'
+ * else:
+ * S = (M'+T') modulo N
+ *
+ * MAI, integer (0 .. N-1) :
+ * MAI = (S + MAIO) modulo N
+ */
+
+static uint8_t rn_table[114] = {
+ 48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
+ 0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
+ 101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
+ 80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
+ 55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
+ 87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
+ 82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
+ 77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
+ 117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
+ 16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
+ 91, 15, 21, 24, 46, 39, 93, 105, 65, 70,
+ 125, 99, 17, 123,
+};
+
+
+static int pow_nbin_mask(int n)
+{
+ int x;
+ x = (n ) |
+ (n >> 1) |
+ (n >> 2) |
+ (n >> 3) |
+ (n >> 4) |
+ (n >> 5) |
+ (n >> 6);
+ return x;
+}
+
+static int16_t rfch_hop_seq_gen(struct gsm_time *t,
+ uint8_t hsn, uint8_t maio,
+ uint8_t n, uint16_t *arfcn_tbl)
+{
+ int mai;
+
+ if (!hsn) {
+ /* cyclic hopping */
+ mai = (t->fn + maio) % n;
+ } else {
+ /* pseudo random hopping */
+ int m, mp, tp, s, pnm;
+
+ pnm = pow_nbin_mask(n);
+
+ m = t->t2 + rn_table[(hsn ^ (t->t1 & 63)) + t->t3];
+ mp = m & pnm;
+
+ if (mp < n)
+ s = mp;
+ else {
+ tp = t->t3 & pnm;
+ s = (mp + tp) % n;
+ }
+
+ mai = (s + maio) % n;
+ }
+
+ return arfcn_tbl ? arfcn_tbl[mai] : mai;
+}
+
+
+/* RF Channel parameters */
+void rfch_get_params(struct gsm_time *t,
+ uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p)
+{
+ if (l1s.dedicated.type == GSM_DCHAN_NONE) {
+ /* Serving cell only */
+ *arfcn_p = l1s.serving_cell.arfcn;
+ *tsc_p = l1s.serving_cell.bsic & 0x7;
+ *tn_p = 0;
+ } else {
+ /* Dedicated channel */
+ if (l1s.dedicated.h) {
+ *arfcn_p = rfch_hop_seq_gen(t,
+ l1s.dedicated.h1.hsn,
+ l1s.dedicated.h1.maio,
+ l1s.dedicated.h1.n,
+ l1s.dedicated.h1.ma);
+ } else {
+ *arfcn_p = l1s.dedicated.h0.arfcn;
+ }
+
+ *tsc_p = l1s.dedicated.tsc;
+ *tn_p = l1s.dedicated.tn;
+ }
+}
+
diff --git a/src/target/firmware/layer1/sched_gsmtime.c b/src/target/firmware/layer1/sched_gsmtime.c
new file mode 100644
index 00000000..6a549e2d
--- /dev/null
+++ b/src/target/firmware/layer1/sched_gsmtime.c
@@ -0,0 +1,119 @@
+/* GSM-Time One-shot Event Scheduler Implementation (on top of TDMA sched) */
+
+/* (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 <errno.h>
+
+#include <debug.h>
+#include <osmocore/linuxlist.h>
+
+#include <layer1/tdma_sched.h>
+#include <layer1/sched_gsmtime.h>
+
+static struct sched_gsmtime_event sched_gsmtime_events[16];
+static LLIST_HEAD(active_evts);
+static LLIST_HEAD(inactive_evts);
+
+/* Scheduling of a tdma_sched_item list one-shot at a givnen GSM time */
+int sched_gsmtime(const struct tdma_sched_item *si, uint32_t fn, uint16_t p3)
+{
+ struct llist_head *lh;
+ struct sched_gsmtime_event *evt, *cur;
+
+ printd("sched_gsmtime(si=%p, fn=%u)\n", si, fn);
+
+ /* obtain a free/inactive event structure */
+ if (llist_empty(&inactive_evts))
+ return -EBUSY;
+ lh = inactive_evts.next;
+ llist_del(lh);
+ evt = llist_entry(lh, struct sched_gsmtime_event, list);
+
+ evt->fn = fn;
+ evt->si = si;
+ evt->p3 = p3;
+
+ /* do a sorted insert into the list, i.e. insert the new
+ * event _before_ the first entry that has a higher fn */
+ llist_for_each_entry(cur, &active_evts, list) {
+ if (cur->fn > evt->fn) {
+ llist_add_tail(lh, &cur->list);
+ return 0;
+ }
+ }
+
+ /* if we reach here, active_evts is empty _OR_ new event
+ * is after all the other events: append at end of list */
+ llist_add_tail(lh, &active_evts);
+
+ return 0;
+}
+
+/* how many TDMA frame ticks should we schedule events ahead? */
+#define SCHEDULE_AHEAD 2
+
+/* how long do we need to tell the DSP in advance what we want to do? */
+#define SCHEDULE_LATENCY 1
+
+/* execute all GSMTIME one-shot events pending for 'fn' */
+int sched_gsmtime_execute(uint32_t fn)
+{
+ struct sched_gsmtime_event *evt, *evt2;
+ int num = 0;
+
+ llist_for_each_entry_safe(evt, evt2, &active_evts, list) {
+ if (evt->fn == fn + SCHEDULE_AHEAD) {
+ printd("sched_gsmtime_execute(time=%u): fn=%u si=%p\n", fn, evt->fn, evt->si);
+ tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY,
+ evt->si, evt->p3);
+ llist_del(&evt->list);
+ /* put event back in list of inactive (free) events */
+ llist_add(&evt->list, &inactive_evts);
+ num++;
+ } if (evt->fn > fn + SCHEDULE_AHEAD) {
+ /* break the loop as our list is ordered */
+ break;
+ }
+ }
+ return num;
+}
+
+void sched_gsmtime_init(void)
+{
+ unsigned int i;
+
+ printd("sched_gsmtime_init()\n");
+
+ for (i = 0; i < ARRAY_SIZE(sched_gsmtime_events); i++)
+ llist_add(&sched_gsmtime_events[i].list, &inactive_evts);
+}
+
+void sched_gsmtime_reset(void)
+{
+ struct sched_gsmtime_event *evt, *evt2;
+
+ llist_for_each_entry_safe(evt, evt2, &active_evts, list) {
+ llist_del(&evt->list);
+ /* put event back in list of inactive (free) events */
+ llist_add(&evt->list, &inactive_evts);
+ }
+}
diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c
new file mode 100644
index 00000000..0c85c57b
--- /dev/null
+++ b/src/target/firmware/layer1/sync.c
@@ -0,0 +1,367 @@
+/* Synchronous part of GSM Layer 1 */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de>
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.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 <string.h>
+#include <stdlib.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <memory.h>
+#include <byteorder.h>
+#include <asm/system.h>
+
+#include <osmocore/gsm_utils.h>
+#include <osmocore/msgb.h>
+#include <calypso/dsp_api.h>
+#include <calypso/irq.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <calypso/dsp.h>
+#include <calypso/timer.h>
+#include <comm/sercomm.h>
+
+#include <abb/twl3025.h>
+
+//#define DEBUG_EVERY_TDMA
+
+#include <layer1/sync.h>
+#include <layer1/afc.h>
+#include <layer1/agc.h>
+#include <layer1/tdma_sched.h>
+#include <layer1/mframe_sched.h>
+#include <layer1/sched_gsmtime.h>
+#include <layer1/tpu_window.h>
+#include <layer1/l23_api.h>
+
+#include <l1a_l23_interface.h>
+
+struct l1s_state l1s;
+
+void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn)
+{
+ ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN);
+
+ if (delta_fn == 1) {
+ ADD_MODULO(time->t2, 1, 26);
+ ADD_MODULO(time->t3, 1, 51);
+
+ /* if the new frame number is a multiple of 51 */
+ if (time->t3 == 0) {
+ ADD_MODULO(time->tc, 1, 8);
+
+ /* if new FN is multiple of 51 and 26 */
+ if (time->t2 == 0)
+ ADD_MODULO(time->t1, 1, 2048);
+ }
+ } else
+ gsm_fn2gsmtime(time, time->fn);
+}
+
+void l1s_time_dump(const struct gsm_time *time)
+{
+ printf("fn=%lu(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3);
+}
+
+/* clip a signed 16bit value at a certain limit */
+int16_t clip_int16(int16_t angle, int16_t clip_at)
+{
+ if (angle > clip_at)
+ angle = clip_at;
+ else if (angle < -clip_at)
+ angle = -clip_at;
+
+ return angle;
+}
+
+int16_t l1s_snr_int(uint16_t snr)
+{
+ return snr >> 10;
+}
+
+uint16_t l1s_snr_fract(uint16_t snr)
+{
+ uint32_t fract = snr & 0x3ff;
+ fract = fract * 1000 / (2 << 10);
+
+ return fract & 0xffff;
+}
+
+#define AFC_MAX_ANGLE 328 /* 0.01 radian in fx1.15 */
+
+/* synchronize the L1S to a new timebase (typically a new cell */
+void synchronize_tdma(struct l1_cell_info *cinfo)
+{
+ int32_t fn_offset;
+ uint32_t tpu_shift = cinfo->time_alignment;
+
+ /* NB detection only works if the TOA of the SB
+ * is within 0...8. We have to add 75 to get an SB TOA of 4. */
+ tpu_shift += 75;
+
+ tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA;
+
+ fn_offset = cinfo->fn_offset - 1;
+
+ /* if we're already very close to the end of the TPU frame, the
+ * next interrupt will basically occur now and we need to
+ * compensate */
+ if (tpu_shift < SWITCH_TIME)
+ fn_offset++;
+
+#if 0 /* probably wrong as we already added "offset" and "shift" above */
+ /* increment the TPU quarter-bit offset */
+ l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE;
+#else
+ l1s.tpu_offset = tpu_shift;
+#endif
+
+ puts("Synchronize_TDMA\n");
+ /* request the TPU to adjust the SYNCHRO and OFFSET registers */
+ tpu_enq_at(SWITCH_TIME);
+ tpu_enq_sync(l1s.tpu_offset);
+#if 0
+ /* FIXME: properly end the TPU window at the emd of l1_sync() */
+ tpu_end_scenario();
+#endif
+
+ /* Change the current time to reflect the new value */
+ l1s_time_inc(&l1s.current_time, fn_offset);
+ l1s.next_time = l1s.current_time;
+ l1s_time_inc(&l1s.next_time, 1);
+
+ /* The serving cell now no longer has a frame or bit offset */
+ cinfo->fn_offset = 0;
+ cinfo->time_alignment = 0;
+}
+
+void l1s_reset_hw(void)
+{
+ dsp_api.w_page = 0;
+ dsp_api.r_page = 0;
+ dsp_api.r_page_used = 0;
+ dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
+ dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
+ dsp_api.ndb->d_dsp_page = 0;
+
+ /* we have to really reset the TPU, otherwise FB detection
+ * somtimes returns wrong TOA values. */
+ tpu_reset(1);
+ tpu_reset(0);
+ tpu_rewind();
+ tpu_enq_wait(5); /* really needed ? */
+ tpu_enq_sync(l1s.tpu_offset);
+ tpu_end_scenario();
+}
+
+/* Timer for detecting lost IRQ */
+#define TIMER_TICKS_PER_TDMA 1875
+
+static int last_timestamp;
+
+static inline void check_lost_frame(void)
+{
+ int diff, timestamp = hwtimer_read(1);
+
+ if (last_timestamp < timestamp)
+ last_timestamp += (4*TIMER_TICKS_PER_TDMA);
+
+ diff = last_timestamp - timestamp;
+ if (diff != 1875)
+ printf("LOST!\n");
+
+ last_timestamp = timestamp;
+}
+
+/* schedule a completion */
+void l1s_compl_sched(enum l1_compl c)
+{
+ unsigned long flags;
+
+ local_firq_save(flags);
+ l1s.scheduled_compl |= (1 << c);
+ local_irq_restore(flags);
+}
+
+/* main routine for synchronous part of layer 1, called by frame interrupt
+ * generated by TPU once every TDMA frame */
+static void l1_sync(void)
+{
+ putchart('+');
+
+ check_lost_frame();
+
+ /* Increment Time */
+ l1s.current_time = l1s.next_time;
+ l1s_time_inc(&l1s.next_time, 1);
+ //l1s_time_dump(&l1s.current_time); putchar(' ');
+
+ dsp_api.frame_ctr++;
+ dsp_api.r_page_used = 0;
+
+ /* Update pointers */
+ if (dsp_api.w_page == 0)
+ dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
+ else
+ dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1;
+
+ if (dsp_api.r_page == 0)
+ dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
+ else
+ dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1;
+
+ /* Reset MCU->DSP page */
+ dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w));
+
+ /* Update AFC */
+ afc_load_dsp();
+
+ if (dsp_api.ndb->d_error_status) {
+ printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status);
+ dsp_api.ndb->d_error_status = 0;
+ }
+
+ /* execute the sched_items that have been scheduled for this
+ * TDMA frame */
+ tdma_sched_execute();
+
+ if (dsp_api.r_page_used) {
+ /* clear and switch the read page */
+ dsp_api_memset((uint16_t *) dsp_api.db_r,
+ sizeof(*dsp_api.db_r));
+
+ /* TSM30 does it (really needed ?):
+ * Set crc result as "SB not found". */
+ dsp_api.db_r->a_sch[0] = (1<<B_SCH_CRC); /* B_SCH_CRC =1, BLUD =0 */
+
+ dsp_api.r_page ^= 1;
+ }
+
+ //dsp_end_scenario();
+
+ /* schedule new / upcoming TDMA items */
+ mframe_schedule();
+ /* schedule new / upcoming one-shot events */
+ sched_gsmtime_execute(l1s.current_time.fn);
+
+ tdma_sched_advance();
+}
+
+/* ABORT command ********************************************************/
+
+static int l1s_abort_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ putchart('A');
+
+ /* similar to l1s_reset_hw() without touching the TPU */
+
+ dsp_api.w_page = 0;
+ dsp_api.r_page = 0;
+ dsp_api.r_page_used = 0;
+ dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
+ dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
+
+ /* Reset task commands. */
+ dsp_api.db_w->d_task_d = NO_DSP_TASK; /* Init. RX task to NO TASK */
+ dsp_api.db_w->d_task_u = NO_DSP_TASK; /* Init. TX task to NO TASK */
+ dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */
+ dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */
+ dsp_api.ndb->d_dsp_page = 0;
+
+ /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */
+ dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT);
+ return 0;
+}
+
+void l1s_dsp_abort(void)
+{
+ /* abort right now */
+ tdma_schedule(0, &l1s_abort_cmd, 0, 0, 0);
+}
+
+void l1s_tx_apc_helper(void)
+{
+ int i;
+
+ /* Load the ApcOffset into the DSP */
+ #define MY_OFFSET 4
+ dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | MY_OFFSET) | 1; /* 2x slope for the GTA-02 ramp */
+
+ /* Load the TX Power into the DSP */
+ /*
+ If the power is too low (below 0 dBm) the ramp is not OK,
+ especially for GSM-1800. However an MS does not send below
+ 0dBm anyway.
+ */
+ dsp_api.db_w->d_power_ctl = ABB_VAL(AUXAPC, 0xC0); /* 2 dBm pulse with offset 4 (GSM-1800) */
+
+ /* Update the ramp according to the PCL */
+ for (i = 0; i < 16; i++)
+ dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, twl3025_default_ramp[i]);
+
+ /* The Ramp Table is sent to ABB only once after RF init routine called */
+ dsp_api.db_w->d_ctrl_abb |= (1 << B_RAMP) | (1 << B_BULRAMPDEL);
+}
+
+/* Interrupt handler */
+static void frame_irq(__unused enum irq_nr nr)
+{
+ l1_sync();
+}
+
+/* reset the layer1 as part of synchronizing to a new cell */
+void l1s_reset(void)
+{
+ l1s.fb.mode = 0;
+
+ /* reset scheduler and hardware */
+ sched_gsmtime_reset();
+ mframe_reset();
+ tdma_sched_reset();
+ l1s_dsp_abort();
+}
+
+void l1s_init(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(l1s.tx_queue); i++)
+ INIT_LLIST_HEAD(&l1s.tx_queue[i]);
+
+ sched_gsmtime_init();
+
+ /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */
+ irq_register_handler(IRQ_TPU_FRAME, &frame_irq);
+ irq_config(IRQ_TPU_FRAME, 1, 1, 0);
+ irq_enable(IRQ_TPU_FRAME);
+
+ /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */
+ hwtimer_enable(1, 1);
+ hwtimer_load(1, (1875*4)-1);
+ hwtimer_config(1, 0, 1);
+ hwtimer_enable(1, 1);
+}
+
diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c
new file mode 100644
index 00000000..82892103
--- /dev/null
+++ b/src/target/firmware/layer1/tdma_sched.c
@@ -0,0 +1,185 @@
+/* TDMA Scheduler Implementation */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <defines.h>
+#include <debug.h>
+#include <osmocore/gsm_utils.h>
+
+#include <layer1/tdma_sched.h>
+#include <layer1/sync.h>
+
+#include <calypso/dsp.h>
+
+/* dummy function to mark end of set */
+int tdma_end_set(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ return 0;
+}
+
+static uint8_t wrap_bucket(uint8_t offset)
+{
+ uint16_t bucket;
+
+ bucket = (l1s.tdma_sched.cur_bucket + offset)
+ % ARRAY_SIZE(l1s.tdma_sched.bucket);
+
+ return bucket;
+}
+
+/* Schedule an item at 'frame_offset' TDMA frames in the future */
+int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint8_t p1, uint8_t p2, uint16_t p3)
+{
+ struct tdma_scheduler *sched = &l1s.tdma_sched;
+ uint8_t bucket_nr = wrap_bucket(frame_offset);
+ struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr];
+ struct tdma_sched_item *sched_item;
+
+ if (bucket->num_items >= ARRAY_SIZE(bucket->item)) {
+ puts("tdma_schedule bucket overflow\n");
+ return -1;
+ }
+
+ sched_item = &bucket->item[bucket->num_items++];
+
+ sched_item->cb = cb;
+ sched_item->p1 = p1;
+ sched_item->p2 = p2;
+ sched_item->p3 = p3;
+
+ return 0;
+}
+
+/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */
+int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint16_t p3)
+{
+ struct tdma_scheduler *sched = &l1s.tdma_sched;
+ uint8_t bucket_nr = wrap_bucket(frame_offset);
+ int i, j;
+
+ for (i = 0, j = 0; 1; i++) {
+ const struct tdma_sched_item *sched_item = &item_set[i];
+ struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr];
+
+ if (sched_item->cb == &tdma_end_set) {
+ /* end of scheduler set, return */
+ break;
+ }
+
+ if (sched_item->cb == NULL) {
+ /* advance to next bucket (== TDMA frame) */
+ bucket_nr = wrap_bucket(++frame_offset);
+ j++;
+ continue;
+ }
+ /* check for bucket overflow */
+ if (bucket->num_items >= ARRAY_SIZE(bucket->item)) {
+ puts("tdma_schedule bucket overflow\n");
+ return -1;
+ }
+ /* copy the item from the set into the current bucket item position */
+ memcpy(&bucket->item[bucket->num_items], sched_item, sizeof(*sched_item));
+ bucket->item[bucket->num_items].p3 = p3;
+ bucket->num_items++;
+ }
+
+ return j;
+}
+
+/* Advance TDMA scheduler to the next bucket */
+void tdma_sched_advance(void)
+{
+ struct tdma_scheduler *sched = &l1s.tdma_sched;
+ uint8_t next_bucket;
+
+ /* advance to the next bucket */
+ next_bucket = wrap_bucket(1);
+ sched->cur_bucket = next_bucket;
+}
+
+/* Execute pre-scheduled events for current frame */
+int tdma_sched_execute(void)
+{
+ struct tdma_scheduler *sched = &l1s.tdma_sched;
+ struct tdma_sched_bucket *bucket;
+ int i, num_events = 0;
+
+ /* determine current bucket */
+ bucket = &sched->bucket[sched->cur_bucket];
+
+ /* iterate over items in this bucket and call callback function */
+ for (i = 0; i < bucket->num_items; i++) {
+ struct tdma_sched_item *item = &bucket->item[i];
+ int rc;
+
+ num_events++;
+
+ rc = item->cb(item->p1, item->p2, item->p3);
+ if (rc < 0) {
+ printf("Error %d during processing of item %u of bucket %u\n",
+ rc, i, sched->cur_bucket);
+ return rc;
+ }
+ /* if the cb() we just called has scheduled more items for the
+ * current TDMA, bucket->num_items will have increased and we
+ * will simply continue to execute them as intended */
+ }
+
+ /* clear/reset the bucket */
+ bucket->num_items = 0;
+
+ /* return number of items that we called */
+ return num_events;
+}
+
+void tdma_sched_reset(void)
+{
+ struct tdma_scheduler *sched = &l1s.tdma_sched;
+ unsigned int bucket_nr;
+
+ for (bucket_nr = 0; bucket_nr < ARRAY_SIZE(sched->bucket); bucket_nr++) {
+ struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr];
+ /* current bucket will be reset by iteration code above! */
+ if (bucket_nr != sched->cur_bucket)
+ bucket->num_items = 0;
+ }
+
+ /* Don't reset cur_bucket, as it would upset the bucket iteration code
+ * in tdma_sched_execute() */
+}
+
+void tdma_sched_dump(void)
+{
+ unsigned int i;
+
+ printf("\n(%2u)", l1s.tdma_sched.cur_bucket);
+ for (i = 0; i < ARRAY_SIZE(l1s.tdma_sched.bucket); i++) {
+ int bucket_nr = wrap_bucket(i);
+ struct tdma_sched_bucket *bucket = &l1s.tdma_sched.bucket[bucket_nr];
+ printf("%u:", bucket->num_items);
+ }
+ putchar('\n');
+}
diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c
new file mode 100644
index 00000000..ef195ca3
--- /dev/null
+++ b/src/target/firmware/layer1/tpu_window.c
@@ -0,0 +1,133 @@
+/* TPU window control routines for Layer 1 */
+
+/* (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 <debug.h>
+#include <stdio.h>
+
+#include <rffe.h>
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <abb/twl3025.h>
+#include <rf/trf6151.h>
+
+#include <layer1/sync.h>
+#include <layer1/tpu_window.h>
+
+/* all units in GSM quarter-bits (923.1ns) */
+#define L1_TDMA_LENGTH_Q 5000
+#define L1_BURST_LENGTH_Q 625 /* L1_TDMA_LENGTH_Q/8 */
+
+#define L1_NB_MARGIN_Q (3 * 4)
+#define L1_SB_MARGIN_Q (23 * 4)
+#define L1_TAIL_DURATION_Q (3 * 4)
+
+/* Sample length as required by the Calypso DSP */
+#define L1_NB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_NB_MARGIN_Q - L1_TAIL_DURATION_Q)
+#define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q)
+#define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */
+#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798)
+#define L1_PW_DURATION_Q 289
+
+#define DSP_SETUP_TIME 66
+
+static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = {
+ [L1_RXWIN_PW] = L1_PW_DURATION_Q,
+ [L1_RXWIN_FB] = L1_FB_DURATION_Q,
+ [L1_RXWIN_SB] = L1_SB_DURATION_Q,
+ [L1_RXWIN_NB] = L1_NB_DURATION_Q,
+};
+
+#define L1_TX_NB_DURATION_Q 626
+#define L1_TX_AB_DURATION_Q 386
+
+static const uint16_t tx_burst_duration[_NUM_L1_TXWIN] = {
+ [L1_TXWIN_NB] = L1_TX_NB_DURATION_Q,
+ [L1_TXWIN_AB] = L1_TX_AB_DURATION_Q,
+};
+
+void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn)
+{
+ int16_t start = DSP_SETUP_TIME;
+ int16_t stop = start + rx_burst_duration[wtype] - 1;
+
+ /* FIXME: AGC */
+ /* FIXME: RF PLL */
+
+ /* Alignement */
+ tpu_enq_offset( (5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000 );
+ tpu_enq_at(5000 - 1000 - (L1_BURST_LENGTH_Q * tn));
+
+ /* window open for TRF6151 */
+ /* FIXME: why do we need the magic value 100 ? */
+ rffe_mode(gsm_arfcn2band(arfcn), 0);
+ trf6151_rx_window(start - 100, arfcn);
+
+ /* Window open for ABB */
+ twl3025_downlink(1, start);
+
+ /* Delay 11 full TDMA frames */
+ if (wtype == L1_RXWIN_FB) {
+ uint8_t i;
+ for (i = 0; i < 11; i++)
+ tpu_enq_at(0);
+
+ stop -= 11 * L1_TDMA_LENGTH_Q;
+ }
+
+ /* Window close for ABB */
+ twl3025_downlink(0, stop);
+
+ /* window close for TRF6151 */
+ trf6151_set_mode(TRF6151_IDLE);
+}
+
+void l1s_tx_win_ctrl(uint16_t arfcn, enum l1_txwin_type wtype, uint8_t pwr, uint8_t tn)
+{
+ /* uplink is three TS after downlink ( "+ 32" gives a TA of 1) */
+ uint16_t offset = (L1_BURST_LENGTH_Q * 3) + 28;
+
+ /* Alignement */
+ tpu_enq_offset( (5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000 );
+ tpu_enq_at(5000 - 10 - (L1_BURST_LENGTH_Q * tn));
+
+#ifdef CONFIG_TX_ENABLE
+ /* window open for TRF6151 and RFFE */
+ rffe_mode(gsm_arfcn2band(arfcn), 1);
+ trf6151_tx_window(offset, arfcn);
+#endif
+
+ /* Window open for ABB */
+ twl3025_uplink(1, offset);
+
+ /* Window close for ABB */
+ twl3025_uplink(0, tx_burst_duration[wtype] + offset + 2); // TODO: "+ 2"
+
+ /* window close for TRF6151 and RFFE */
+ trf6151_set_mode(TRF6151_IDLE);
+}
+
+void tpu_end_scenario(void)
+{
+ tpu_enq_sleep();
+ tpu_enable(1);
+}
diff --git a/src/target/firmware/lib/Makefile b/src/target/firmware/lib/Makefile
new file mode 100644
index 00000000..987857c9
--- /dev/null
+++ b/src/target/firmware/lib/Makefile
@@ -0,0 +1,7 @@
+
+LIBRARIES+=mini
+mini_DIR=lib
+mini_SRCS=vsprintf.c string.c ctype.c printf.c console.c ctors.c \
+ changebit.S clearbit.S div64.S lib1funcs.S memcpy.S memset.S setbit.S testchangebit.S testclearbit.S testsetbit.S
+
+
diff --git a/src/target/firmware/lib/bitops.h b/src/target/firmware/lib/bitops.h
new file mode 100644
index 00000000..428c9a63
--- /dev/null
+++ b/src/target/firmware/lib/bitops.h
@@ -0,0 +1,33 @@
+ .macro bitop, instr
+ and r2, r0, #7
+ mov r3, #1
+ mov r3, r3, lsl r2
+ save_and_disable_irqs ip
+ ldrb r2, [r1, r0, lsr #3]
+ \instr r2, r2, r3
+ strb r2, [r1, r0, lsr #3]
+ restore_irqs ip
+ mov pc, lr
+ .endm
+
+/**
+ * testop - implement a test_and_xxx_bit operation.
+ * @instr: operational instruction
+ * @store: store instruction
+ *
+ * Note: we can trivially conditionalise the store instruction
+ * to avoid dirting the data cache.
+ */
+ .macro testop, instr, store
+ add r1, r1, r0, lsr #3
+ and r3, r0, #7
+ mov r0, #1
+ save_and_disable_irqs ip
+ ldrb r2, [r1]
+ tst r2, r0, lsl r3
+ \instr r2, r2, r0, lsl r3
+ \store r2, [r1]
+ restore_irqs ip
+ moveq r0, #0
+ mov pc, lr
+ .endm
diff --git a/src/target/firmware/lib/changebit.S b/src/target/firmware/lib/changebit.S
new file mode 100644
index 00000000..7c709fb3
--- /dev/null
+++ b/src/target/firmware/lib/changebit.S
@@ -0,0 +1,21 @@
+/*
+ * linux/arch/arm/lib/changebit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+#include "bitops.h"
+ .text
+
+/* Purpose : Function to change a bit
+ * Prototype: int change_bit(int bit, void *addr)
+ */
+ENTRY(_change_bit_be)
+ eor r0, r0, #0x18 @ big endian byte ordering
+ENTRY(_change_bit_le)
+ bitop eor
diff --git a/src/target/firmware/lib/clearbit.S b/src/target/firmware/lib/clearbit.S
new file mode 100644
index 00000000..cb48f7ac
--- /dev/null
+++ b/src/target/firmware/lib/clearbit.S
@@ -0,0 +1,22 @@
+/*
+ * linux/arch/arm/lib/clearbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+#include "bitops.h"
+ .text
+
+/*
+ * Purpose : Function to clear a bit
+ * Prototype: int clear_bit(int bit, void *addr)
+ */
+ENTRY(_clear_bit_be)
+ eor r0, r0, #0x18 @ big endian byte ordering
+ENTRY(_clear_bit_le)
+ bitop bic
diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c
new file mode 100644
index 00000000..2ec028a0
--- /dev/null
+++ b/src/target/firmware/lib/console.c
@@ -0,0 +1,190 @@
+/* Ringbuffer based serial console layer, imported from OpenPCD */
+
+/* (C) 2006-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 <string.h>
+#include <console.h>
+#include <calypso/uart.h>
+
+#include <asm/system.h>
+
+struct cons {
+ char buf[CONS_RB_SIZE];
+ char *next_inbyte;
+ char *next_outbyte;
+ int initialized;
+};
+static struct cons cons;
+
+void cons_init(void)
+{
+ memset(cons.buf, 0, sizeof(cons.buf));
+ cons.next_inbyte = &cons.buf[0];
+ cons.next_outbyte = &cons.buf[0];
+ cons.initialized = 1;
+}
+
+/* determine how many bytes are left in the ringbuffer without overwriting
+ bytes that haven't been written to the console yet */
+static int __cons_rb_space(void)
+{
+ if (cons.next_inbyte == cons.next_outbyte)
+ return sizeof(cons.buf)-1;
+ else if (cons.next_outbyte > cons.next_inbyte)
+ return (cons.next_outbyte - cons.next_inbyte) -1;
+ else
+ return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte);
+}
+
+/* pull one char out of debug ring buffer */
+static int cons_rb_pull(char *ret)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ if (cons.next_outbyte == cons.next_inbyte) {
+ local_irq_restore(flags);
+ return -1;
+ }
+
+ *ret = *cons.next_outbyte;
+
+ cons.next_outbyte++;
+ if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) {
+ cons.next_outbyte = &cons.buf[0];
+ }
+#if 0
+ else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) {
+ cons.next_outbyte -= sizeof(cons.buf);
+ }
+#endif
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+/* returns if everything was flushed (1) or if there's more to flush (0) */
+static void __rb_flush_wait(void)
+{
+ char ch;
+ while (cons_rb_pull(&ch) >= 0)
+ uart_putchar_wait(CONS_UART_NR, ch);
+}
+
+/* returns if everything was flushed (1) or if there's more to flush (0) */
+static int __rb_flush(void)
+{
+ while (!uart_tx_busy(CONS_UART_NR)) {
+ char ch;
+ if (cons_rb_pull(&ch) < 0) {
+ /* no more data to write, disable interest in Tx FIFO interrupts */
+ return 1;
+ }
+ uart_putchar_nb(CONS_UART_NR, ch);
+ }
+
+ /* if we reach here, UART Tx FIFO is busy again */
+ return 0;
+}
+
+/* flush pending data from debug ring buffer to serial port */
+int cons_rb_flush(void)
+{
+ return __rb_flush();
+}
+
+/* Append bytes to ring buffer, not more than we have left! */
+static void __cons_rb_append(const char *data, int len)
+{
+ if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) {
+ int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte;
+ /* copy the first part before we wrap */
+ memcpy(cons.next_inbyte, data, before_tail);
+ data += before_tail;
+ len -= before_tail;
+ /* reset the buffer */
+ cons.next_inbyte = &cons.buf[0];
+ }
+ memcpy(cons.next_inbyte, data, len);
+ cons.next_inbyte += len;
+}
+
+/* append bytes to the ringbuffer, do one wrap */
+int cons_rb_append(const char *data, int len)
+{
+ unsigned long flags;
+ int bytes_left;
+ const char *data_cur;
+
+ /* we will never be able to write more than the console buffer */
+ if (len > (int) sizeof(cons.buf))
+ len = sizeof(cons.buf);
+
+ local_irq_save(flags);
+
+ bytes_left = __cons_rb_space();
+ data_cur = data;
+
+ if (len > bytes_left) {
+ /* append what we can */
+ __cons_rb_append(data_cur, bytes_left);
+ /* busy-wait for all characters to be transmitted */
+ __rb_flush_wait();
+ /* fill it with the remaining bytes */
+ len -= bytes_left;
+ data_cur += bytes_left;
+ }
+ __cons_rb_append(data_cur, len);
+
+ /* we want to get Tx FIFO interrupts */
+ uart_irq_enable(CONS_UART_NR, UART_IRQ_TX_EMPTY, 1);
+
+ local_irq_restore(flags);
+
+ return len;
+}
+
+int cons_puts(const char *s)
+{
+ if (cons.initialized) {
+ return cons_rb_append(s, strlen(s));
+ } else {
+ /* if the console is not active yet, we need to fall back */
+ int i = strlen(s);
+ while (i--)
+ uart_putchar_wait(CONS_UART_NR, *s++);
+ return i;
+ }
+}
+
+int cons_putchar(char c)
+{
+ if (cons.initialized)
+ return cons_rb_append(&c, 1);
+ else {
+ /* if the console is not active yet, we need to fall back */
+ uart_putchar_wait(CONS_UART_NR, c);
+ return 0;
+ }
+}
diff --git a/src/target/firmware/lib/copy_template.S b/src/target/firmware/lib/copy_template.S
new file mode 100644
index 00000000..cab355c0
--- /dev/null
+++ b/src/target/firmware/lib/copy_template.S
@@ -0,0 +1,255 @@
+/*
+ * linux/arch/arm/lib/copy_template.s
+ *
+ * Code template for optimized memory copy functions
+ *
+ * Author: Nicolas Pitre
+ * Created: Sep 28, 2005
+ * Copyright: MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This can be used to enable code to cacheline align the source pointer.
+ * Experiments on tested architectures (StrongARM and XScale) didn't show
+ * this a worthwhile thing to do. That might be different in the future.
+ */
+//#define CALGN(code...) code
+#define CALGN(code...)
+
+/*
+ * Theory of operation
+ * -------------------
+ *
+ * This file provides the core code for a forward memory copy used in
+ * the implementation of memcopy(), copy_to_user() and copy_from_user().
+ *
+ * The including file must define the following accessor macros
+ * according to the need of the given function:
+ *
+ * ldr1w ptr reg abort
+ *
+ * This loads one word from 'ptr', stores it in 'reg' and increments
+ * 'ptr' to the next word. The 'abort' argument is used for fixup tables.
+ *
+ * ldr4w ptr reg1 reg2 reg3 reg4 abort
+ * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
+ *
+ * This loads four or eight words starting from 'ptr', stores them
+ * in provided registers and increments 'ptr' past those words.
+ * The'abort' argument is used for fixup tables.
+ *
+ * ldr1b ptr reg cond abort
+ *
+ * Similar to ldr1w, but it loads a byte and increments 'ptr' one byte.
+ * It also must apply the condition code if provided, otherwise the
+ * "al" condition is assumed by default.
+ *
+ * str1w ptr reg abort
+ * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
+ * str1b ptr reg cond abort
+ *
+ * Same as their ldr* counterparts, but data is stored to 'ptr' location
+ * rather than being loaded.
+ *
+ * enter reg1 reg2
+ *
+ * Preserve the provided registers on the stack plus any additional
+ * data as needed by the implementation including this code. Called
+ * upon code entry.
+ *
+ * exit reg1 reg2
+ *
+ * Restore registers with the values previously saved with the
+ * 'preserv' macro. Called upon code termination.
+ */
+
+
+ enter r4, lr
+
+ subs r2, r2, #4
+ blt 8f
+ ands ip, r0, #3
+ PLD( pld [r1, #0] )
+ bne 9f
+ ands ip, r1, #3
+ bne 10f
+
+1: subs r2, r2, #(28)
+ stmfd sp!, {r5 - r8}
+ blt 5f
+
+ CALGN( ands ip, r1, #31 )
+ CALGN( rsb r3, ip, #32 )
+ CALGN( sbcnes r4, r3, r2 ) @ C is always set here
+ CALGN( bcs 2f )
+ CALGN( adr r4, 6f )
+ CALGN( subs r2, r2, r3 ) @ C gets set
+ CALGN( add pc, r4, ip )
+
+ PLD( pld [r1, #0] )
+2: PLD( subs r2, r2, #96 )
+ PLD( pld [r1, #28] )
+ PLD( blt 4f )
+ PLD( pld [r1, #60] )
+ PLD( pld [r1, #92] )
+
+3: PLD( pld [r1, #124] )
+4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
+ subs r2, r2, #32
+ str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
+ bge 3b
+ PLD( cmn r2, #96 )
+ PLD( bge 4b )
+
+5: ands ip, r2, #28
+ rsb ip, ip, #32
+ addne pc, pc, ip @ C is always clear here
+ b 7f
+6: nop
+ ldr1w r1, r3, abort=20f
+ ldr1w r1, r4, abort=20f
+ ldr1w r1, r5, abort=20f
+ ldr1w r1, r6, abort=20f
+ ldr1w r1, r7, abort=20f
+ ldr1w r1, r8, abort=20f
+ ldr1w r1, lr, abort=20f
+
+ add pc, pc, ip
+ nop
+ nop
+ str1w r0, r3, abort=20f
+ str1w r0, r4, abort=20f
+ str1w r0, r5, abort=20f
+ str1w r0, r6, abort=20f
+ str1w r0, r7, abort=20f
+ str1w r0, r8, abort=20f
+ str1w r0, lr, abort=20f
+
+ CALGN( bcs 2b )
+
+7: ldmfd sp!, {r5 - r8}
+
+8: movs r2, r2, lsl #31
+ ldr1b r1, r3, ne, abort=21f
+ ldr1b r1, r4, cs, abort=21f
+ ldr1b r1, ip, cs, abort=21f
+ str1b r0, r3, ne, abort=21f
+ str1b r0, r4, cs, abort=21f
+ str1b r0, ip, cs, abort=21f
+
+ exit r4, pc
+
+9: rsb ip, ip, #4
+ cmp ip, #2
+ ldr1b r1, r3, gt, abort=21f
+ ldr1b r1, r4, ge, abort=21f
+ ldr1b r1, lr, abort=21f
+ str1b r0, r3, gt, abort=21f
+ str1b r0, r4, ge, abort=21f
+ subs r2, r2, ip
+ str1b r0, lr, abort=21f
+ blt 8b
+ ands ip, r1, #3
+ beq 1b
+
+10: bic r1, r1, #3
+ cmp ip, #2
+ ldr1w r1, lr, abort=21f
+ beq 17f
+ bgt 18f
+
+
+ .macro forward_copy_shift pull push
+
+ subs r2, r2, #28
+ blt 14f
+
+ CALGN( ands ip, r1, #31 )
+ CALGN( rsb ip, ip, #32 )
+ CALGN( sbcnes r4, ip, r2 ) @ C is always set here
+ CALGN( subcc r2, r2, ip )
+ CALGN( bcc 15f )
+
+11: stmfd sp!, {r5 - r9}
+
+ PLD( pld [r1, #0] )
+ PLD( subs r2, r2, #96 )
+ PLD( pld [r1, #28] )
+ PLD( blt 13f )
+ PLD( pld [r1, #60] )
+ PLD( pld [r1, #92] )
+
+12: PLD( pld [r1, #124] )
+13: ldr4w r1, r4, r5, r6, r7, abort=19f
+ mov r3, lr, pull #\pull
+ subs r2, r2, #32
+ ldr4w r1, r8, r9, ip, lr, abort=19f
+ orr r3, r3, r4, push #\push
+ mov r4, r4, pull #\pull
+ orr r4, r4, r5, push #\push
+ mov r5, r5, pull #\pull
+ orr r5, r5, r6, push #\push
+ mov r6, r6, pull #\pull
+ orr r6, r6, r7, push #\push
+ mov r7, r7, pull #\pull
+ orr r7, r7, r8, push #\push
+ mov r8, r8, pull #\pull
+ orr r8, r8, r9, push #\push
+ mov r9, r9, pull #\pull
+ orr r9, r9, ip, push #\push
+ mov ip, ip, pull #\pull
+ orr ip, ip, lr, push #\push
+ str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f
+ bge 12b
+ PLD( cmn r2, #96 )
+ PLD( bge 13b )
+
+ ldmfd sp!, {r5 - r9}
+
+14: ands ip, r2, #28
+ beq 16f
+
+15: mov r3, lr, pull #\pull
+ ldr1w r1, lr, abort=21f
+ subs ip, ip, #4
+ orr r3, r3, lr, push #\push
+ str1w r0, r3, abort=21f
+ bgt 15b
+ CALGN( cmp r2, #0 )
+ CALGN( bge 11b )
+
+16: sub r1, r1, #(\push / 8)
+ b 8b
+
+ .endm
+
+
+ forward_copy_shift pull=8 push=24
+
+17: forward_copy_shift pull=16 push=16
+
+18: forward_copy_shift pull=24 push=8
+
+
+/*
+ * Abort preamble and completion macros.
+ * If a fixup handler is required then those macros must surround it.
+ * It is assumed that the fixup code will handle the private part of
+ * the exit macro.
+ */
+
+ .macro copy_abort_preamble
+19: ldmfd sp!, {r5 - r9}
+ b 21f
+20: ldmfd sp!, {r5 - r8}
+21:
+ .endm
+
+ .macro copy_abort_end
+ ldmfd sp!, {r4, pc}
+ .endm
+
diff --git a/src/target/firmware/lib/ctors.c b/src/target/firmware/lib/ctors.c
new file mode 100644
index 00000000..982169df
--- /dev/null
+++ b/src/target/firmware/lib/ctors.c
@@ -0,0 +1,15 @@
+
+/* iterate over list of constructor functions and call each element */
+void do_global_ctors(const char *_ctors_start, const char *ctors_end)
+{
+ typedef void (*func_ptr)(void);
+ func_ptr *func, *ctors_start = (func_ptr *) _ctors_start;
+
+ /* skip the first entry, as it contains the number of
+ * constructors which we don't use */
+ ctors_start++;
+
+ for (func = ctors_start;
+ *func && (func != (func_ptr *) ctors_end); func++)
+ (*func)();
+}
diff --git a/src/target/firmware/lib/ctype.c b/src/target/firmware/lib/ctype.c
new file mode 100644
index 00000000..f3732140
--- /dev/null
+++ b/src/target/firmware/lib/ctype.c
@@ -0,0 +1,34 @@
+/*
+ * linux/lib/ctype.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <ctype.h>
+
+unsigned char _ctype[] = {
+_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
+_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
+_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
+_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
+_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
+_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
+_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
+_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
+_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
+_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
+_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
+_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
+_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
+_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
+_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
+_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
+_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
+
diff --git a/src/target/firmware/lib/div64.S b/src/target/firmware/lib/div64.S
new file mode 100644
index 00000000..7eeef50c
--- /dev/null
+++ b/src/target/firmware/lib/div64.S
@@ -0,0 +1,200 @@
+/*
+ * linux/arch/arm/lib/div64.S
+ *
+ * Optimized computation of 64-bit dividend / 32-bit divisor
+ *
+ * Author: Nicolas Pitre
+ * Created: Oct 5, 2003
+ * Copyright: Monta Vista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/linkage.h>
+
+#ifdef __ARMEB__
+#define xh r0
+#define xl r1
+#define yh r2
+#define yl r3
+#else
+#define xl r0
+#define xh r1
+#define yl r2
+#define yh r3
+#endif
+
+/*
+ * __do_div64: perform a division with 64-bit dividend and 32-bit divisor.
+ *
+ * Note: Calling convention is totally non standard for optimal code.
+ * This is meant to be used by do_div() from include/asm/div64.h only.
+ *
+ * Input parameters:
+ * xh-xl = dividend (clobbered)
+ * r4 = divisor (preserved)
+ *
+ * Output values:
+ * yh-yl = result
+ * xh = remainder
+ *
+ * Clobbered regs: xl, ip
+ */
+
+ENTRY(__do_div64)
+
+ @ Test for easy paths first.
+ subs ip, r4, #1
+ bls 9f @ divisor is 0 or 1
+ tst ip, r4
+ beq 8f @ divisor is power of 2
+
+ @ See if we need to handle upper 32-bit result.
+ cmp xh, r4
+ mov yh, #0
+ blo 3f
+
+ @ Align divisor with upper part of dividend.
+ @ The aligned divisor is stored in yl preserving the original.
+ @ The bit position is stored in ip.
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz yl, r4
+ clz ip, xh
+ sub yl, yl, ip
+ mov ip, #1
+ mov ip, ip, lsl yl
+ mov yl, r4, lsl yl
+
+#else
+
+ mov yl, r4
+ mov ip, #1
+1: cmp yl, #0x80000000
+ cmpcc yl, xh
+ movcc yl, yl, lsl #1
+ movcc ip, ip, lsl #1
+ bcc 1b
+
+#endif
+
+ @ The division loop for needed upper bit positions.
+ @ Break out early if dividend reaches 0.
+2: cmp xh, yl
+ orrcs yh, yh, ip
+ subcss xh, xh, yl
+ movnes ip, ip, lsr #1
+ mov yl, yl, lsr #1
+ bne 2b
+
+ @ See if we need to handle lower 32-bit result.
+3: cmp xh, #0
+ mov yl, #0
+ cmpeq xl, r4
+ movlo xh, xl
+ movlo pc, lr
+
+ @ The division loop for lower bit positions.
+ @ Here we shift remainer bits leftwards rather than moving the
+ @ divisor for comparisons, considering the carry-out bit as well.
+ mov ip, #0x80000000
+4: movs xl, xl, lsl #1
+ adcs xh, xh, xh
+ beq 6f
+ cmpcc xh, r4
+5: orrcs yl, yl, ip
+ subcs xh, xh, r4
+ movs ip, ip, lsr #1
+ bne 4b
+ mov pc, lr
+
+ @ The top part of remainder became zero. If carry is set
+ @ (the 33th bit) this is a false positive so resume the loop.
+ @ Otherwise, if lower part is also null then we are done.
+6: bcs 5b
+ cmp xl, #0
+ moveq pc, lr
+
+ @ We still have remainer bits in the low part. Bring them up.
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz xh, xl @ we know xh is zero here so...
+ add xh, xh, #1
+ mov xl, xl, lsl xh
+ mov ip, ip, lsr xh
+
+#else
+
+7: movs xl, xl, lsl #1
+ mov ip, ip, lsr #1
+ bcc 7b
+
+#endif
+
+ @ Current remainder is now 1. It is worthless to compare with
+ @ divisor at this point since divisor can not be smaller than 3 here.
+ @ If possible, branch for another shift in the division loop.
+ @ If no bit position left then we are done.
+ movs ip, ip, lsr #1
+ mov xh, #1
+ bne 4b
+ mov pc, lr
+
+8: @ Division by a power of 2: determine what that divisor order is
+ @ then simply shift values around
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz ip, r4
+ rsb ip, ip, #31
+
+#else
+
+ mov yl, r4
+ cmp r4, #(1 << 16)
+ mov ip, #0
+ movhs yl, yl, lsr #16
+ movhs ip, #16
+
+ cmp yl, #(1 << 8)
+ movhs yl, yl, lsr #8
+ addhs ip, ip, #8
+
+ cmp yl, #(1 << 4)
+ movhs yl, yl, lsr #4
+ addhs ip, ip, #4
+
+ cmp yl, #(1 << 2)
+ addhi ip, ip, #3
+ addls ip, ip, yl, lsr #1
+
+#endif
+
+ mov yh, xh, lsr ip
+ mov yl, xl, lsr ip
+ rsb ip, ip, #32
+ orr yl, yl, xh, lsl ip
+ mov xh, xl, lsl ip
+ mov xh, xh, lsr ip
+ mov pc, lr
+
+ @ eq -> division by 1: obvious enough...
+9: moveq yl, xl
+ moveq yh, xh
+ moveq xh, #0
+ moveq pc, lr
+
+ @ Division by 0:
+ str lr, [sp, #-8]!
+ bl __div0
+
+ @ as wrong as it could be...
+ mov yl, #0
+ mov yh, #0
+ mov xh, #0
+ ldr pc, [sp], #8
+
diff --git a/src/target/firmware/lib/lib1funcs.S b/src/target/firmware/lib/lib1funcs.S
new file mode 100644
index 00000000..b02a85eb
--- /dev/null
+++ b/src/target/firmware/lib/lib1funcs.S
@@ -0,0 +1,334 @@
+/*
+ * linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines
+ *
+ * Author: Nicolas Pitre <nico@cam.org>
+ * - contributed to gcc-3.4 on Sep 30, 2003
+ * - adapted for the Linux kernel on Oct 2, 2003
+ */
+
+/* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc.
+
+This file 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, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that 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; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+
+
+.macro ARM_DIV_BODY dividend, divisor, result, curbit
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz \curbit, \divisor
+ clz \result, \dividend
+ sub \result, \curbit, \result
+ mov \curbit, #1
+ mov \divisor, \divisor, lsl \result
+ mov \curbit, \curbit, lsl \result
+ mov \result, #0
+
+#else
+
+ @ Initially shift the divisor left 3 bits if possible,
+ @ set curbit accordingly. This allows for curbit to be located
+ @ at the left end of each 4 bit nibbles in the division loop
+ @ to save one loop in most cases.
+ tst \divisor, #0xe0000000
+ moveq \divisor, \divisor, lsl #3
+ moveq \curbit, #8
+ movne \curbit, #1
+
+ @ Unless the divisor is very big, shift it up in multiples of
+ @ four bits, since this is the amount of unwinding in the main
+ @ division loop. Continue shifting until the divisor is
+ @ larger than the dividend.
+1: cmp \divisor, #0x10000000
+ cmplo \divisor, \dividend
+ movlo \divisor, \divisor, lsl #4
+ movlo \curbit, \curbit, lsl #4
+ blo 1b
+
+ @ For very big divisors, we must shift it a bit at a time, or
+ @ we will be in danger of overflowing.
+1: cmp \divisor, #0x80000000
+ cmplo \divisor, \dividend
+ movlo \divisor, \divisor, lsl #1
+ movlo \curbit, \curbit, lsl #1
+ blo 1b
+
+ mov \result, #0
+
+#endif
+
+ @ Division loop
+1: cmp \dividend, \divisor
+ subhs \dividend, \dividend, \divisor
+ orrhs \result, \result, \curbit
+ cmp \dividend, \divisor, lsr #1
+ subhs \dividend, \dividend, \divisor, lsr #1
+ orrhs \result, \result, \curbit, lsr #1
+ cmp \dividend, \divisor, lsr #2
+ subhs \dividend, \dividend, \divisor, lsr #2
+ orrhs \result, \result, \curbit, lsr #2
+ cmp \dividend, \divisor, lsr #3
+ subhs \dividend, \dividend, \divisor, lsr #3
+ orrhs \result, \result, \curbit, lsr #3
+ cmp \dividend, #0 @ Early termination?
+ movnes \curbit, \curbit, lsr #4 @ No, any more bits to do?
+ movne \divisor, \divisor, lsr #4
+ bne 1b
+
+.endm
+
+
+.macro ARM_DIV2_ORDER divisor, order
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz \order, \divisor
+ rsb \order, \order, #31
+
+#else
+
+ cmp \divisor, #(1 << 16)
+ movhs \divisor, \divisor, lsr #16
+ movhs \order, #16
+ movlo \order, #0
+
+ cmp \divisor, #(1 << 8)
+ movhs \divisor, \divisor, lsr #8
+ addhs \order, \order, #8
+
+ cmp \divisor, #(1 << 4)
+ movhs \divisor, \divisor, lsr #4
+ addhs \order, \order, #4
+
+ cmp \divisor, #(1 << 2)
+ addhi \order, \order, #3
+ addls \order, \order, \divisor, lsr #1
+
+#endif
+
+.endm
+
+
+.macro ARM_MOD_BODY dividend, divisor, order, spare
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz \order, \divisor
+ clz \spare, \dividend
+ sub \order, \order, \spare
+ mov \divisor, \divisor, lsl \order
+
+#else
+
+ mov \order, #0
+
+ @ Unless the divisor is very big, shift it up in multiples of
+ @ four bits, since this is the amount of unwinding in the main
+ @ division loop. Continue shifting until the divisor is
+ @ larger than the dividend.
+1: cmp \divisor, #0x10000000
+ cmplo \divisor, \dividend
+ movlo \divisor, \divisor, lsl #4
+ addlo \order, \order, #4
+ blo 1b
+
+ @ For very big divisors, we must shift it a bit at a time, or
+ @ we will be in danger of overflowing.
+1: cmp \divisor, #0x80000000
+ cmplo \divisor, \dividend
+ movlo \divisor, \divisor, lsl #1
+ addlo \order, \order, #1
+ blo 1b
+
+#endif
+
+ @ Perform all needed substractions to keep only the reminder.
+ @ Do comparisons in batch of 4 first.
+ subs \order, \order, #3 @ yes, 3 is intended here
+ blt 2f
+
+1: cmp \dividend, \divisor
+ subhs \dividend, \dividend, \divisor
+ cmp \dividend, \divisor, lsr #1
+ subhs \dividend, \dividend, \divisor, lsr #1
+ cmp \dividend, \divisor, lsr #2
+ subhs \dividend, \dividend, \divisor, lsr #2
+ cmp \dividend, \divisor, lsr #3
+ subhs \dividend, \dividend, \divisor, lsr #3
+ cmp \dividend, #1
+ mov \divisor, \divisor, lsr #4
+ subges \order, \order, #4
+ bge 1b
+
+ tst \order, #3
+ teqne \dividend, #0
+ beq 5f
+
+ @ Either 1, 2 or 3 comparison/substractions are left.
+2: cmn \order, #2
+ blt 4f
+ beq 3f
+ cmp \dividend, \divisor
+ subhs \dividend, \dividend, \divisor
+ mov \divisor, \divisor, lsr #1
+3: cmp \dividend, \divisor
+ subhs \dividend, \dividend, \divisor
+ mov \divisor, \divisor, lsr #1
+4: cmp \dividend, \divisor
+ subhs \dividend, \dividend, \divisor
+5:
+.endm
+
+
+ENTRY(__udivsi3)
+ENTRY(__aeabi_uidiv)
+
+ subs r2, r1, #1
+ moveq pc, lr
+ bcc Ldiv0
+ cmp r0, r1
+ bls 11f
+ tst r1, r2
+ beq 12f
+
+ ARM_DIV_BODY r0, r1, r2, r3
+
+ mov r0, r2
+ mov pc, lr
+
+11: moveq r0, #1
+ movne r0, #0
+ mov pc, lr
+
+12: ARM_DIV2_ORDER r1, r2
+
+ mov r0, r0, lsr r2
+ mov pc, lr
+
+
+ENTRY(__umodsi3)
+
+ subs r2, r1, #1 @ compare divisor with 1
+ bcc Ldiv0
+ cmpne r0, r1 @ compare dividend with divisor
+ moveq r0, #0
+ tsthi r1, r2 @ see if divisor is power of 2
+ andeq r0, r0, r2
+ movls pc, lr
+
+ ARM_MOD_BODY r0, r1, r2, r3
+
+ mov pc, lr
+
+
+ENTRY(__divsi3)
+ENTRY(__aeabi_idiv)
+
+ cmp r1, #0
+ eor ip, r0, r1 @ save the sign of the result.
+ beq Ldiv0
+ rsbmi r1, r1, #0 @ loops below use unsigned.
+ subs r2, r1, #1 @ division by 1 or -1 ?
+ beq 10f
+ movs r3, r0
+ rsbmi r3, r0, #0 @ positive dividend value
+ cmp r3, r1
+ bls 11f
+ tst r1, r2 @ divisor is power of 2 ?
+ beq 12f
+
+ ARM_DIV_BODY r3, r1, r0, r2
+
+ cmp ip, #0
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+10: teq ip, r0 @ same sign ?
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+11: movlo r0, #0
+ moveq r0, ip, asr #31
+ orreq r0, r0, #1
+ mov pc, lr
+
+12: ARM_DIV2_ORDER r1, r2
+
+ cmp ip, #0
+ mov r0, r3, lsr r2
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+
+ENTRY(__modsi3)
+
+ cmp r1, #0
+ beq Ldiv0
+ rsbmi r1, r1, #0 @ loops below use unsigned.
+ movs ip, r0 @ preserve sign of dividend
+ rsbmi r0, r0, #0 @ if negative make positive
+ subs r2, r1, #1 @ compare divisor with 1
+ cmpne r0, r1 @ compare dividend with divisor
+ moveq r0, #0
+ tsthi r1, r2 @ see if divisor is power of 2
+ andeq r0, r0, r2
+ bls 10f
+
+ ARM_MOD_BODY r0, r1, r2, r3
+
+10: cmp ip, #0
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+ENTRY(__aeabi_uidivmod)
+
+ stmfd sp!, {r0, r1, ip, lr}
+ bl __aeabi_uidiv
+ ldmfd sp!, {r1, r2, ip, lr}
+ mul r3, r0, r2
+ sub r1, r1, r3
+ mov pc, lr
+
+ENTRY(__aeabi_idivmod)
+
+ stmfd sp!, {r0, r1, ip, lr}
+ bl __aeabi_idiv
+ ldmfd sp!, {r1, r2, ip, lr}
+ mul r3, r0, r2
+ sub r1, r1, r3
+ mov pc, lr
+
+Ldiv0:
+
+ str lr, [sp, #-8]!
+ bl __div0
+ mov r0, #0 @ About as wrong as it could be.
+ ldr pc, [sp], #8
+
+ENTRY(__div0)
+ mov pc, lr
diff --git a/src/target/firmware/lib/memcpy.S b/src/target/firmware/lib/memcpy.S
new file mode 100644
index 00000000..2bbd5692
--- /dev/null
+++ b/src/target/firmware/lib/memcpy.S
@@ -0,0 +1,59 @@
+/*
+ * linux/arch/arm/lib/memcpy.S
+ *
+ * Author: Nicolas Pitre
+ * Created: Sep 28, 2005
+ * Copyright: MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+
+ .macro ldr1w ptr reg abort
+ ldr \reg, [\ptr], #4
+ .endm
+
+ .macro ldr4w ptr reg1 reg2 reg3 reg4 abort
+ ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4}
+ .endm
+
+ .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
+ ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
+ .endm
+
+ .macro ldr1b ptr reg cond=al abort
+ ldr\cond\()b \reg, [\ptr], #1
+ .endm
+
+ .macro str1w ptr reg abort
+ str \reg, [\ptr], #4
+ .endm
+
+ .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
+ stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
+ .endm
+
+ .macro str1b ptr reg cond=al abort
+ str\cond\()b \reg, [\ptr], #1
+ .endm
+
+ .macro enter reg1 reg2
+ stmdb sp!, {r0, \reg1, \reg2}
+ .endm
+
+ .macro exit reg1 reg2
+ ldmfd sp!, {r0, \reg1, \reg2}
+ .endm
+
+ .text
+
+/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */
+
+ENTRY(memcpy)
+
+#include "copy_template.S"
+
diff --git a/src/target/firmware/lib/memset.S b/src/target/firmware/lib/memset.S
new file mode 100644
index 00000000..04e254a8
--- /dev/null
+++ b/src/target/firmware/lib/memset.S
@@ -0,0 +1,80 @@
+/*
+ * linux/arch/arm/lib/memset.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ASM optimised string functions
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+
+ .text
+ .align 5
+ .word 0
+
+1: subs r2, r2, #4 @ 1 do we have enough
+ blt 5f @ 1 bytes to align with?
+ cmp r3, #2 @ 1
+ strltb r1, [r0], #1 @ 1
+ strleb r1, [r0], #1 @ 1
+ strb r1, [r0], #1 @ 1
+ add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3))
+/*
+ * The pointer is now aligned and the length is adjusted. Try doing the
+ * memzero again.
+ */
+
+ENTRY(memset)
+ ands r3, r0, #3 @ 1 unaligned?
+ bne 1b @ 1
+/*
+ * we know that the pointer in r0 is aligned to a word boundary.
+ */
+ orr r1, r1, r1, lsl #8
+ orr r1, r1, r1, lsl #16
+ mov r3, r1
+ cmp r2, #16
+ blt 4f
+/*
+ * We need an extra register for this loop - save the return address and
+ * use the LR
+ */
+ str lr, [sp, #-4]!
+ mov ip, r1
+ mov lr, r1
+
+2: subs r2, r2, #64
+ stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
+ stmgeia r0!, {r1, r3, ip, lr}
+ stmgeia r0!, {r1, r3, ip, lr}
+ stmgeia r0!, {r1, r3, ip, lr}
+ bgt 2b
+ LOADREGS(eqfd, sp!, {pc}) @ Now <64 bytes to go.
+/*
+ * No need to correct the count; we're only testing bits from now on
+ */
+ tst r2, #32
+ stmneia r0!, {r1, r3, ip, lr}
+ stmneia r0!, {r1, r3, ip, lr}
+ tst r2, #16
+ stmneia r0!, {r1, r3, ip, lr}
+ ldr lr, [sp], #4
+
+4: tst r2, #8
+ stmneia r0!, {r1, r3}
+ tst r2, #4
+ strne r1, [r0], #4
+/*
+ * When we get here, we've got less than 4 bytes to zero. We
+ * may have an unaligned pointer as well.
+ */
+5: tst r2, #2
+ strneb r1, [r0], #1
+ strneb r1, [r0], #1
+ tst r2, #1
+ strneb r1, [r0], #1
+ RETINSTR(mov,pc,lr)
diff --git a/src/target/firmware/lib/printf.c b/src/target/firmware/lib/printf.c
new file mode 100644
index 00000000..a4fc6876
--- /dev/null
+++ b/src/target/firmware/lib/printf.c
@@ -0,0 +1,19 @@
+
+#include <stdio.h>
+#include <stdarg.h>
+
+static char printf_buffer[1024];
+
+int printf(const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = vsnprintf(printf_buffer, sizeof(printf_buffer), fmt, args);
+ va_end(args);
+
+ puts(printf_buffer);
+
+ return r;
+}
diff --git a/src/target/firmware/lib/setbit.S b/src/target/firmware/lib/setbit.S
new file mode 100644
index 00000000..9009bc1e
--- /dev/null
+++ b/src/target/firmware/lib/setbit.S
@@ -0,0 +1,22 @@
+/*
+ * linux/arch/arm/lib/setbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+#include "bitops.h"
+ .text
+
+/*
+ * Purpose : Function to set a bit
+ * Prototype: int set_bit(int bit, void *addr)
+ */
+ENTRY(_set_bit_be)
+ eor r0, r0, #0x18 @ big endian byte ordering
+ENTRY(_set_bit_le)
+ bitop orr
diff --git a/src/target/firmware/lib/string.c b/src/target/firmware/lib/string.c
new file mode 100644
index 00000000..97036528
--- /dev/null
+++ b/src/target/firmware/lib/string.c
@@ -0,0 +1,50 @@
+/*
+ * linux/lib/string.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ *
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
+ * Matthew Hawkins <matt@mh.dropbear.id.au>
+ * - Kissed strtok() goodbye
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <ctype.h>
+
+
+#ifndef __HAVE_ARCH_STRNLEN
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char *s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+size_t strlen(const char *s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
diff --git a/src/target/firmware/lib/testchangebit.S b/src/target/firmware/lib/testchangebit.S
new file mode 100644
index 00000000..37c303e3
--- /dev/null
+++ b/src/target/firmware/lib/testchangebit.S
@@ -0,0 +1,18 @@
+/*
+ * linux/arch/arm/lib/testchangebit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+#include "bitops.h"
+ .text
+
+ENTRY(_test_and_change_bit_be)
+ eor r0, r0, #0x18 @ big endian byte ordering
+ENTRY(_test_and_change_bit_le)
+ testop eor, strb
diff --git a/src/target/firmware/lib/testclearbit.S b/src/target/firmware/lib/testclearbit.S
new file mode 100644
index 00000000..985c3996
--- /dev/null
+++ b/src/target/firmware/lib/testclearbit.S
@@ -0,0 +1,18 @@
+/*
+ * linux/arch/arm/lib/testclearbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+#include "bitops.h"
+ .text
+
+ENTRY(_test_and_clear_bit_be)
+ eor r0, r0, #0x18 @ big endian byte ordering
+ENTRY(_test_and_clear_bit_le)
+ testop bicne, strneb
diff --git a/src/target/firmware/lib/testsetbit.S b/src/target/firmware/lib/testsetbit.S
new file mode 100644
index 00000000..4a8a164b
--- /dev/null
+++ b/src/target/firmware/lib/testsetbit.S
@@ -0,0 +1,18 @@
+/*
+ * linux/arch/arm/lib/testsetbit.S
+ *
+ * Copyright (C) 1995-1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/linkage.h>
+#include <asm/assembler.h>
+#include "bitops.h"
+ .text
+
+ENTRY(_test_and_set_bit_be)
+ eor r0, r0, #0x18 @ big endian byte ordering
+ENTRY(_test_and_set_bit_le)
+ testop orreq, streqb
diff --git a/src/target/firmware/lib/vsprintf.c b/src/target/firmware/lib/vsprintf.c
new file mode 100644
index 00000000..80e8c1ad
--- /dev/null
+++ b/src/target/firmware/lib/vsprintf.c
@@ -0,0 +1,847 @@
+/*
+ * linux/lib/vsprintf.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/*
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb 1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
+ * - scnprintf and vscnprintf
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <asm/div64.h>
+
+/**
+ * strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long strtoul(const char *cp,char **endp,unsigned int base)
+{
+ unsigned long result = 0,value;
+
+ if (!base) {
+ base = 10;
+ if (*cp == '0') {
+ base = 8;
+ cp++;
+ if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+ cp++;
+ base = 16;
+ }
+ }
+ } else if (base == 16) {
+ if (cp[0] == '0' && toupper(cp[1]) == 'X')
+ cp += 2;
+ }
+ while (isxdigit(*cp) &&
+ (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+ result = result*base + value;
+ cp++;
+ }
+ if (endp)
+ *endp = (char *)cp;
+ return result;
+}
+
+
+/**
+ * strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long strtol(const char *cp,char **endp,unsigned int base)
+{
+ if(*cp=='-')
+ return -strtoul(cp+1,endp,base);
+ return strtoul(cp,endp,base);
+}
+
+
+/**
+ * strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long strtoull(const char *cp,char **endp,unsigned int base)
+{
+ unsigned long long result = 0,value;
+
+ if (!base) {
+ base = 10;
+ if (*cp == '0') {
+ base = 8;
+ cp++;
+ if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
+ cp++;
+ base = 16;
+ }
+ }
+ } else if (base == 16) {
+ if (cp[0] == '0' && toupper(cp[1]) == 'X')
+ cp += 2;
+ }
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+ ? toupper(*cp) : *cp)-'A'+10) < base) {
+ result = result*base + value;
+ cp++;
+ }
+ if (endp)
+ *endp = (char *)cp;
+ return result;
+}
+
+
+/**
+ * strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long strtoll(const char *cp,char **endp,unsigned int base)
+{
+ if(*cp=='-')
+ return -strtoull(cp+1,endp,base);
+ return strtoull(cp,endp,base);
+}
+
+static int skip_atoi(const char **s)
+{
+ int i=0;
+
+ while (isdigit(**s))
+ i = i*10 + *((*s)++) - '0';
+ return i;
+}
+
+#define ZEROPAD 1 /* pad with zero */
+#define SIGN 2 /* unsigned/signed long */
+#define PLUS 4 /* show plus */
+#define SPACE 8 /* space if plus */
+#define LEFT 16 /* left justified */
+#define SPECIAL 32 /* 0x */
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
+
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
+{
+ char c,sign,tmp[66];
+ const char *digits;
+ static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ int i;
+
+ digits = (type & LARGE) ? large_digits : small_digits;
+ if (type & LEFT)
+ type &= ~ZEROPAD;
+ if (base < 2 || base > 36)
+ return NULL;
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN) {
+ if ((signed long long) num < 0) {
+ sign = '-';
+ num = - (signed long long) num;
+ size--;
+ } else if (type & PLUS) {
+ sign = '+';
+ size--;
+ } else if (type & SPACE) {
+ sign = ' ';
+ size--;
+ }
+ }
+ if (type & SPECIAL) {
+ if (base == 16)
+ size -= 2;
+ else if (base == 8)
+ size--;
+ }
+ i = 0;
+ if (num == 0)
+ tmp[i++]='0';
+ else while (num != 0)
+ tmp[i++] = digits[do_div(num,base)];
+ if (i > precision)
+ precision = i;
+ size -= precision;
+ if (!(type&(ZEROPAD+LEFT))) {
+ while(size-->0) {
+ if (buf <= end)
+ *buf = ' ';
+ ++buf;
+ }
+ }
+ if (sign) {
+ if (buf <= end)
+ *buf = sign;
+ ++buf;
+ }
+ if (type & SPECIAL) {
+ if (base==8) {
+ if (buf <= end)
+ *buf = '0';
+ ++buf;
+ } else if (base==16) {
+ if (buf <= end)
+ *buf = '0';
+ ++buf;
+ if (buf <= end)
+ *buf = digits[33];
+ ++buf;
+ }
+ }
+ if (!(type & LEFT)) {
+ while (size-- > 0) {
+ if (buf <= end)
+ *buf = c;
+ ++buf;
+ }
+ }
+ while (i < precision--) {
+ if (buf <= end)
+ *buf = '0';
+ ++buf;
+ }
+ while (i-- > 0) {
+ if (buf <= end)
+ *buf = tmp[i];
+ ++buf;
+ }
+ while (size-- > 0) {
+ if (buf <= end)
+ *buf = ' ';
+ ++buf;
+ }
+ return buf;
+}
+
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf. If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int len;
+ unsigned long long num;
+ int i, base;
+ char *str, *end, c;
+ const char *s;
+
+ int flags; /* flags to number() */
+
+ int field_width; /* width of output field */
+ int precision; /* min. # of digits for integers; max
+ number of chars for from string */
+ int qualifier; /* 'h', 'l', or 'L' for integer fields */
+ /* 'z' support added 23/7/1999 S.H. */
+ /* 'z' changed to 'Z' --davidm 1/25/99 */
+ /* 't' added for ptrdiff_t */
+
+ /* Reject out-of-range values early */
+ if ((int) size < 0) {
+ return 0;
+ }
+
+ str = buf;
+ end = buf + size - 1;
+
+ if (end < buf - 1) {
+ end = ((void *) -1);
+ size = end - buf + 1;
+ }
+
+ for (; *fmt ; ++fmt) {
+ if (*fmt != '%') {
+ if (str <= end)
+ *str = *fmt;
+ ++str;
+ continue;
+ }
+
+ /* process flags */
+ flags = 0;
+ repeat:
+ ++fmt; /* this also skips first '%' */
+ switch (*fmt) {
+ case '-': flags |= LEFT; goto repeat;
+ case '+': flags |= PLUS; goto repeat;
+ case ' ': flags |= SPACE; goto repeat;
+ case '#': flags |= SPECIAL; goto repeat;
+ case '0': flags |= ZEROPAD; goto repeat;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ field_width = skip_atoi(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ field_width = va_arg(args, int);
+ if (field_width < 0) {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* get the precision */
+ precision = -1;
+ if (*fmt == '.') {
+ ++fmt;
+ if (isdigit(*fmt))
+ precision = skip_atoi(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ precision = 0;
+ }
+
+ /* get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+ *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
+ qualifier = *fmt;
+ ++fmt;
+ if (qualifier == 'l' && *fmt == 'l') {
+ qualifier = 'L';
+ ++fmt;
+ }
+ }
+
+ /* default base */
+ base = 10;
+
+ switch (*fmt) {
+ case 'c':
+ if (!(flags & LEFT)) {
+ while (--field_width > 0) {
+ if (str <= end)
+ *str = ' ';
+ ++str;
+ }
+ }
+ c = (unsigned char) va_arg(args, int);
+ if (str <= end)
+ *str = c;
+ ++str;
+ while (--field_width > 0) {
+ if (str <= end)
+ *str = ' ';
+ ++str;
+ }
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+
+ len = strnlen(s, precision);
+
+ if (!(flags & LEFT)) {
+ while (len < field_width--) {
+ if (str <= end)
+ *str = ' ';
+ ++str;
+ }
+ }
+ for (i = 0; i < len; ++i) {
+ if (str <= end)
+ *str = *s;
+ ++str; ++s;
+ }
+ while (len < field_width--) {
+ if (str <= end)
+ *str = ' ';
+ ++str;
+ }
+ continue;
+
+ case 'p':
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = number(str, end,
+ (unsigned long) va_arg(args, void *),
+ 16, field_width, precision, flags);
+ continue;
+
+
+ case 'n':
+ /* FIXME:
+ * What does C99 say about the overflow case here? */
+ if (qualifier == 'l') {
+ long * ip = va_arg(args, long *);
+ *ip = (str - buf);
+ } else if (qualifier == 'Z' || qualifier == 'z') {
+ size_t * ip = va_arg(args, size_t *);
+ *ip = (str - buf);
+ } else {
+ int * ip = va_arg(args, int *);
+ *ip = (str - buf);
+ }
+ continue;
+
+ case '%':
+ if (str <= end)
+ *str = '%';
+ ++str;
+ continue;
+
+ /* integer number formats - set up the flags and "break" */
+ case 'o':
+ base = 8;
+ break;
+
+ case 'X':
+ flags |= LARGE;
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+ case 'u':
+ break;
+
+ default:
+ if (str <= end)
+ *str = '%';
+ ++str;
+ if (*fmt) {
+ if (str <= end)
+ *str = *fmt;
+ ++str;
+ } else {
+ --fmt;
+ }
+ continue;
+ }
+ if (qualifier == 'L')
+ num = va_arg(args, long long);
+ else if (qualifier == 'l') {
+ num = va_arg(args, unsigned long);
+ if (flags & SIGN)
+ num = (signed long) num;
+ } else if (qualifier == 'Z' || qualifier == 'z') {
+ num = va_arg(args, size_t);
+ } else if (qualifier == 't') {
+ num = va_arg(args, long);
+ } else if (qualifier == 'h') {
+ num = (unsigned short) va_arg(args, int);
+ if (flags & SIGN)
+ num = (signed short) num;
+ } else {
+ num = va_arg(args, unsigned int);
+ if (flags & SIGN)
+ num = (signed int) num;
+ }
+ str = number(str, end, num, base,
+ field_width, precision, flags);
+ }
+ if (str <= end)
+ *str = '\0';
+ else if (size > 0)
+ /* don't write out a null byte if the buf size is zero */
+ *end = '\0';
+ /* the trailing null byte doesn't count towards the total
+ * ++str;
+ */
+ return str-buf;
+}
+
+
+/**
+ * vscnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which have been written into
+ * the @buf not including the trailing '\0'. If @size is <= 0 the function
+ * returns 0.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want scnprintf instead.
+ */
+int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ unsigned int i;
+
+ i=vsnprintf(buf,size,fmt,args);
+ return (i >= size) ? (size - 1) : i;
+}
+
+
+/**
+ * snprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ *
+ * The return value is the number of characters which would be
+ * generated for the given input, excluding the trailing null,
+ * as per ISO C99. If the return is greater than or equal to
+ * @size, the resulting string is truncated.
+ */
+int snprintf(char * buf, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i=vsnprintf(buf,size,fmt,args);
+ va_end(args);
+ return i;
+}
+
+
+/**
+ * scnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ *
+ * The return value is the number of characters written into @buf not including
+ * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is
+ * greater than or equal to @size, the resulting string is truncated.
+ */
+
+int scnprintf(char * buf, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ unsigned int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+ return (i >= size) ? (size - 1) : i;
+}
+
+/**
+ * vsprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The function returns the number of characters written
+ * into @buf. Use vsnprintf or vscnprintf in order to avoid
+ * buffer overflows.
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want sprintf instead.
+ */
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+ return vsnprintf(buf, INT_MAX, fmt, args);
+}
+
+
+/**
+ * sprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ *
+ * The function returns the number of characters written
+ * into @buf. Use snprintf or scnprintf in order to avoid
+ * buffer overflows.
+ */
+int sprintf(char * buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i=vsnprintf(buf, INT_MAX, fmt, args);
+ va_end(args);
+ return i;
+}
+
+
+/**
+ * vsscanf - Unformat a buffer into a list of arguments
+ * @buf: input buffer
+ * @fmt: format of buffer
+ * @args: arguments
+ */
+int vsscanf(const char * buf, const char * fmt, va_list args)
+{
+ const char *str = buf;
+ char *next;
+ char digit;
+ int num = 0;
+ int qualifier;
+ int base;
+ int field_width;
+ int is_sign = 0;
+
+ while(*fmt && *str) {
+ /* skip any white space in format */
+ /* white space in format matchs any amount of
+ * white space, including none, in the input.
+ */
+ if (isspace(*fmt)) {
+ while (isspace(*fmt))
+ ++fmt;
+ while (isspace(*str))
+ ++str;
+ }
+
+ /* anything that is not a conversion must match exactly */
+ if (*fmt != '%' && *fmt) {
+ if (*fmt++ != *str++)
+ break;
+ continue;
+ }
+
+ if (!*fmt)
+ break;
+ ++fmt;
+
+ /* skip this conversion.
+ * advance both strings to next white space
+ */
+ if (*fmt == '*') {
+ while (!isspace(*fmt) && *fmt)
+ fmt++;
+ while (!isspace(*str) && *str)
+ str++;
+ continue;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ field_width = skip_atoi(&fmt);
+
+ /* get conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
+ *fmt == 'Z' || *fmt == 'z') {
+ qualifier = *fmt++;
+ if (qualifier == *fmt) {
+ if (qualifier == 'h') {
+ qualifier = 'H';
+ fmt++;
+ } else if (qualifier == 'l') {
+ qualifier = 'L';
+ fmt++;
+ }
+ }
+ }
+ base = 10;
+ is_sign = 0;
+
+ if (!*fmt || !*str)
+ break;
+
+ switch(*fmt++) {
+ case 'c':
+ {
+ char *s = (char *) va_arg(args,char*);
+ if (field_width == -1)
+ field_width = 1;
+ do {
+ *s++ = *str++;
+ } while (--field_width > 0 && *str);
+ num++;
+ }
+ continue;
+ case 's':
+ {
+ char *s = (char *) va_arg(args, char *);
+ if(field_width == -1)
+ field_width = INT_MAX;
+ /* first, skip leading white space in buffer */
+ while (isspace(*str))
+ str++;
+
+ /* now copy until next white space */
+ while (*str && !isspace(*str) && field_width--) {
+ *s++ = *str++;
+ }
+ *s = '\0';
+ num++;
+ }
+ continue;
+ case 'n':
+ /* return number of characters read so far */
+ {
+ int *i = (int *)va_arg(args,int*);
+ *i = str - buf;
+ }
+ continue;
+ case 'o':
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ base = 16;
+ break;
+ case 'i':
+ base = 0;
+ case 'd':
+ is_sign = 1;
+ case 'u':
+ break;
+ case '%':
+ /* looking for '%' in str */
+ if (*str++ != '%')
+ return num;
+ continue;
+ default:
+ /* invalid format; stop here */
+ return num;
+ }
+
+ /* have some sort of integer conversion.
+ * first, skip white space in buffer.
+ */
+ while (isspace(*str))
+ str++;
+
+ digit = *str;
+ if (is_sign && digit == '-')
+ digit = *(str + 1);
+
+ if (!digit
+ || (base == 16 && !isxdigit(digit))
+ || (base == 10 && !isdigit(digit))
+ || (base == 8 && (!isdigit(digit) || digit > '7'))
+ || (base == 0 && !isdigit(digit)))
+ break;
+
+ switch(qualifier) {
+ case 'H': /* that's 'hh' in format */
+ if (is_sign) {
+ signed char *s = (signed char *) va_arg(args,signed char *);
+ *s = (signed char) strtol(str,&next,base);
+ } else {
+ unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
+ *s = (unsigned char) strtoul(str, &next, base);
+ }
+ break;
+ case 'h':
+ if (is_sign) {
+ short *s = (short *) va_arg(args,short *);
+ *s = (short) strtol(str,&next,base);
+ } else {
+ unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
+ *s = (unsigned short) strtoul(str, &next, base);
+ }
+ break;
+ case 'l':
+ if (is_sign) {
+ long *l = (long *) va_arg(args,long *);
+ *l = strtol(str,&next,base);
+ } else {
+ unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
+ *l = strtoul(str,&next,base);
+ }
+ break;
+ case 'L':
+ if (is_sign) {
+ long long *l = (long long*) va_arg(args,long long *);
+ *l = strtoll(str,&next,base);
+ } else {
+ unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
+ *l = strtoull(str,&next,base);
+ }
+ break;
+ case 'Z':
+ case 'z':
+ {
+ size_t *s = (size_t*) va_arg(args,size_t*);
+ *s = (size_t) strtoul(str,&next,base);
+ }
+ break;
+ default:
+ if (is_sign) {
+ int *i = (int *) va_arg(args, int*);
+ *i = (int) strtol(str,&next,base);
+ } else {
+ unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
+ *i = (unsigned int) strtoul(str,&next,base);
+ }
+ break;
+ }
+ num++;
+
+ if (!next)
+ break;
+ str = next;
+ }
+ return num;
+}
+
+
+/**
+ * sscanf - Unformat a buffer into a list of arguments
+ * @buf: input buffer
+ * @fmt: formatting of buffer
+ * @...: resulting arguments
+ */
+int sscanf(const char * buf, const char * fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args,fmt);
+ i = vsscanf(buf,fmt,args);
+ va_end(args);
+ return i;
+}
+
+/* generic puts() implementation independent of who provides putchar() */
+int puts(const char *s)
+{
+#ifdef ARCH_HAS_CONSOLE
+ return _puts(s);
+#else
+ while (1) {
+ char c = *s++;
+ if (c == 0)
+ return;
+ putchar(c);
+ }
+ return 0;
+#endif
+}
diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c
new file mode 100644
index 00000000..4d0541d7
--- /dev/null
+++ b/src/target/firmware/rf/trf6151.c
@@ -0,0 +1,502 @@
+/* Driver for RF Transceiver Circuit (TRF6151) */
+
+/* (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 <memory.h>
+#include <keypad.h>
+#include <osmocore/gsm_utils.h>
+
+#include <calypso/tpu.h>
+#include <calypso/tsp.h>
+#include <layer1/agc.h>
+#include <rffe.h>
+
+#include <rf/trf6151.h>
+
+enum trf6151_reg {
+ REG_RX = 0, /* RF general settings */
+ REG_PLL = 1, /* PLL settings */
+ REG_PWR = 2, /* Power on/off funcitonal blocks */
+ REG_CFG = 3, /* Transceiver and PA controller settings */
+ REG_TEST1 = 4,
+ REG_TEST2 = 5,
+ REG_TEST3 = 6,
+ REG_TEST4 = 7,
+ _MAX_REG
+};
+
+/* REG_RX */
+#define RX_READ_EN (1 << 7)
+#define RX_CAL_MODE (1 << 8)
+#define RX_RF_GAIN_HIGH (3 << 9)
+#define RX_VGA_GAIN_SHIFT 11
+
+/* REG_PWR */
+#define PWR_BANDGAP_SHIFT 3
+#define PWR_BANDGAP_OFF (0 << PWR_BANDGAP_SHIFT)
+#define PWR_BANDGAP_ON_SPEEDUP (2 << PWR_BANDGAP_SHIFT)
+#define PWR_BANDGAP_ON (3 << PWR_BANDGAP_SHIFT)
+#define PWR_REGUL_ON (1 << 5)
+#define PWR_SYNTHE_OFF (0)
+#define PWR_SYNTHE_RX_ON (1 << 9)
+#define PWR_SYNTHE_TX_ON (1 << 10)
+#define PWR_RX_MODE (1 << 11)
+#define PWR_TX_MODE (1 << 13)
+#define PWR_PACTRL_APC (1 << 14)
+#define PWR_PACTRL_APCEN (1 << 15)
+
+/* REG_CFG */
+#define CFG_TX_LOOP_MANU (1 << 3)
+#define CFG_PACTLR_IDIOD_30uA (0 << 4)
+#define CFG_PACTLR_IDIOD_300uA (1 << 4)
+#define CFG_PACTLR_RES_OPEN (0 << 10)
+#define CFG_PACTLR_RES_150k (1 << 10)
+#define CFG_PACTLR_RES_300k (2 << 10)
+#define CFG_PACTLR_CAP_0pF (0 << 12)
+#define CFG_PACTLR_CAP_12p5F (1 << 12)
+#define CFG_PACTLR_CAP_25pF (3 << 12)
+#define CFG_PACTLR_CAP_50pF (2 << 12)
+#define CFG_TEMP_SENSOR (1 << 14)
+#define CFG_ILOGIC_INIT_DIS (1 << 15)
+
+/* FIXME: This must be defined in the RFFE configuration */
+#define TRF6151_TSP_UID 2
+#define TRF6151_PACTRL_CFG (CFG_PACTLR_RES_OPEN|CFG_PACTLR_CAP_0pF|CFG_PACTLR_IDIOD_30uA)
+
+#define PLL_VAL(a, b) ((a << 3) | (((b)-64) << 9))
+
+/* All values in qbits unless otherwise speciifed */
+#define TRF6151_LDO_DELAY_TS 6 /* six TDMA frames (at least 25ms) */
+#define TRF6151_RX_PLL_DELAY 184 /* 170 us */
+#define TRF6151_TX_PLL_DELAY 260 /* 240 us */
+
+uint16_t rf_arfcn = 871; /* TODO: this needs to be private */
+static uint16_t rf_band;
+
+static uint8_t trf6151_vga_dbm = 40;
+static int trf6151_gain_high = 1;
+
+static uint16_t trf6151_reg_cache[_MAX_REG] = {
+ [REG_RX] = 0x9E00,
+ [REG_PLL] = 0x0000,
+ [REG_PWR] = 0x0000,
+ [REG_CFG] = 0x2980,
+};
+
+/* Write to a TRF6151 register (4 TPU instructions) */
+static void trf6151_reg_write(uint16_t reg, uint16_t val)
+{
+ printd("trf6151_reg_write(reg=%u, val=0x%04x)\n", reg, val);
+ /* each TSP write takes 4 TPU instructions */
+ tsp_write(TRF6151_TSP_UID, 16, (reg | val));
+ trf6151_reg_cache[reg] = val;
+}
+
+int trf6151_set_gain(uint8_t dbm, int high)
+{
+ uint16_t reg = trf6151_reg_cache[REG_RX] & 0x07ff;
+ printf("trf6151_set_gain(%u, %d)\n", dbm, high);
+
+ if (dbm < 14 || dbm > 40)
+ return -1;
+
+ /* clear the gain bits first */
+ reg &= ~((0x1F) << RX_VGA_GAIN_SHIFT);
+ /* OR-in the new gain value */
+ reg |= (6 + (dbm-14)/2) << RX_VGA_GAIN_SHIFT;
+
+ if (high)
+ reg |= RX_RF_GAIN_HIGH;
+ else
+ reg &= ~RX_RF_GAIN_HIGH;
+
+ trf6151_reg_write(REG_RX, reg);
+
+ return 0;
+}
+
+#define SCALE_100KHZ 100
+
+/* Compute TRF6151 PLL valuese for all 4 RX bands */
+static uint16_t trf6151_pll_rx(uint32_t freq_khz)
+{
+ uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */
+ uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */
+ uint32_t l;
+ uint32_t a, b; /* The PLL multipliers we want to compute */
+
+ /* L = 4 for low band, 2 for high band */
+ if (freq_khz < 1000000)
+ l = 4;
+ else
+ l = 2;
+
+ /* To compute B, we assume A is zero */
+ b = (freq_100khz * 65 * l) / (64 * 26 * 10);
+
+ if ((l == 4 && (b < 135 || b > 150)) ||
+ (l == 2 && (b < 141 || b > 155)))
+ printf("Frequency %u kHz is out of spec\n", freq_khz);
+
+ /* Compute PLL frequency assuming A == 0 */
+ fb_100khz = (b * 64 * 26 * 10) / (65 * l);
+
+ /* Compute how many 100kHz units A needs to add */
+ a = freq_100khz - fb_100khz;
+
+ if (l == 2)
+ a = a / 2;
+
+ /* since all frequencies are expanded a factor of 10, we don't need to multiply A */
+ printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b);
+
+ /* return value in trf6151 register layout form */
+ return PLL_VAL(a, b);
+}
+
+/* Compute TRF6151 PLL TX values for GSM900 and GSM1800 only! */
+static uint16_t trf6151_pll_tx(uint32_t freq_khz)
+{
+ uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */
+ uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */
+ uint32_t l, r, m;
+ uint32_t a, b; /* The PLL multipliers we want to compute */
+
+ /* L = 4 for low band, 2 for high band */
+ if (freq_khz < 1000000) {
+ r = 35;
+ l = 4;
+ m = 52;
+ } else {
+ r = 70;
+ l = 2;
+ m = 26;
+ }
+
+ /* To compute B, we assume A is zero */
+ b = (freq_100khz * r * l * m) / (64 * 26 * 10 * (m + l));
+
+ if ((l == 4 && (b < 68 || b > 71)) ||
+ (l == 2 && (b < 133 || b > 149)))
+ printf("Frequency %u kHz is out of spec\n", freq_khz);
+
+ /* Compute PLL frequency assuming A == 0 */
+ fb_100khz = (b * 64 * 26 * 10 * (m + l)) / (r * l * m);
+
+ /* Compute how many 100kHz units A needs to add */
+ a = freq_100khz - fb_100khz;
+
+ a = a / 2;
+
+ /* since all frequencies are expanded a factor of 10, we don't need to multiply A */
+ printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b);
+
+ /* return value in trf6151 register layout form */
+ return PLL_VAL(a, b);
+}
+
+enum trf6151_pwr_unit {
+ TRF1651_PACTLR_APC,
+ TRF6151_PACTRL_APCEN,
+ TRF6151_TRANSMITTER,
+ TRF6151_REGULATORS,
+};
+
+enum trf6151_gsm_band {
+ GSM900 = 1,
+ GSM1800 = 2,
+ GSM850_LOW = 4,
+ GSM850_HIGH = 5,
+ GSM1900 = 6,
+};
+
+static inline void trf6151_reset(void)
+{
+ /* pull the nRESET line low */
+ tsp_act_disable((1 << 0));
+ tpu_enq_wait(50);
+ /* release nRESET */
+ tsp_act_enable((1 << 0));
+}
+
+void trf6151_init(void)
+{
+ /* Configure TSPEN0, which is connected to TWL3025,
+ * FIXME: why is this here and not in the TWL3025 driver? */
+ tsp_setup(0, 1, 0, 0);
+ /* Configure TSPEN2, which is connected ot TRF6151 STROBE */
+ tsp_setup(TRF6151_TSP_UID, 0, 1, 1);
+
+ trf6151_reset();
+
+ /* configure TRF6151 for operation */
+ trf6151_power(1);
+ trf6151_reg_write(REG_CFG, TRF6151_PACTRL_CFG | CFG_ILOGIC_INIT_DIS);
+
+ /* FIXME: Uplink / Downlink Calibration */
+}
+
+void trf6151_power(int on)
+{
+ if (on) {
+ trf6151_reg_write(REG_PWR, PWR_REGUL_ON | PWR_BANDGAP_ON);
+ /* wait until regulators are stable (25ms == 27100 qbits) */
+ tpu_enq_wait(5000);
+ tpu_enq_wait(5000);
+ tpu_enq_wait(5000);
+ tpu_enq_wait(5000);
+ tpu_enq_wait(5000);
+ tpu_enq_wait(2100);
+ } else
+ trf6151_reg_write(REG_PWR, PWR_BANDGAP_ON);
+}
+
+/* Set the operational mode of the TRF6151 chip */
+void trf6151_set_mode(enum trf6151_mode mode)
+{
+ uint16_t pwr = (PWR_REGUL_ON | PWR_BANDGAP_ON | (rf_band<<6));
+
+ switch (mode) {
+ case TRF6151_IDLE:
+ /* should we switch of the RF gain for power saving? */
+ break;
+ case TRF6151_RX:
+ pwr |= (PWR_SYNTHE_RX_ON | PWR_RX_MODE);
+ break;
+ case TRF6151_TX:
+#if 0
+ pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE);
+#else // Dieter: we should turn power control on (for TPU: check timing and order !)
+ pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE | PWR_PACTRL_APC | PWR_PACTRL_APCEN); // Dieter: TODO
+#endif
+ break;
+ }
+ trf6151_reg_write(REG_PWR, pwr);
+}
+
+static void trf6151_band_select(enum trf6151_gsm_band band)
+{
+ uint16_t pwr = trf6151_reg_cache[REG_PWR];
+
+ pwr &= ~(3 << 6);
+ pwr |= (band << 6);
+
+ trf6151_reg_write(REG_PWR, pwr);
+}
+
+/* Set ARFCN. Takes 2 reg_write, i.e. 8 TPU instructions */
+void trf6151_set_arfcn(uint16_t arfcn, int uplink)
+{
+ uint32_t freq_khz;
+
+ switch (gsm_arfcn2band(arfcn)) {
+ case GSM_BAND_850:
+ rf_band = GSM850_LOW; /* FIXME: what about HIGH */
+ break;
+ case GSM_BAND_900:
+ rf_band = GSM900;
+ break;
+ case GSM_BAND_1800:
+ rf_band = GSM1800;
+ break;
+ case GSM_BAND_1900:
+ rf_band = GSM1900;
+ break;
+ case GSM_BAND_450:
+ case GSM_BAND_480:
+ case GSM_BAND_750:
+ case GSM_BAND_810:
+ printf("Unsupported rf_band.\n");
+ break;
+ }
+
+ trf6151_band_select(rf_band);
+
+ freq_khz = gsm_arfcn2freq10(arfcn, uplink) * 100;
+ printd("ARFCN %u -> %u kHz\n", arfcn, freq_khz);
+
+ if (uplink == 0)
+ trf6151_reg_write(REG_PLL, trf6151_pll_rx(freq_khz));
+ else {
+ if (rf_band != GSM900 && rf_band != GSM1800) {
+ printf("TX only supports GSM900/1800\n");
+ return;
+ }
+ trf6151_reg_write(REG_PLL, trf6151_pll_tx(freq_khz));
+ }
+
+ rf_arfcn = arfcn; // TODO: arfcn is referenced at other places
+}
+
+void trf6151_calib_dc_offs(void)
+{
+ uint16_t rx = trf6151_reg_cache[REG_RX];
+
+ /* Set RX CAL Mode bit, it will re-set automatically */
+ trf6151_reg_write(REG_RX, rx | RX_CAL_MODE);
+ /* DC offset calibration can take up to 50us, i.e. 54.16 * 923ns*/
+ tpu_enq_wait(55);
+}
+
+/* Frontend gain can be switched high or low (dB) */
+#define TRF6151_FE_GAIN_LOW 7
+#define TRF6151_FE_GAIN_HIGH 27
+
+/* VGA at baseband can be adjusted in this range (dB) */
+#define TRF6151_VGA_GAIN_MIN 14
+#define TRF6151_VGA_GAIN_MAX 40
+
+uint8_t trf6151_get_gain(void)
+{
+ uint16_t vga, reg_rx = trf6151_reg_cache[REG_RX];
+ uint8_t gain = 0;
+
+ switch ((reg_rx >> 9) & 3) {
+ case 0:
+ gain += TRF6151_FE_GAIN_LOW;
+ break;
+ case 3:
+ gain += TRF6151_FE_GAIN_HIGH;
+ break;
+ }
+
+ vga = (reg_rx >> RX_VGA_GAIN_SHIFT) & 0x1f;
+ if (vga < 6)
+ vga = 6;
+
+ gain += TRF6151_VGA_GAIN_MIN + (vga - 6) * 2;
+
+ return gain;
+}
+
+void trf6151_test(uint16_t arfcn)
+{
+ /* Select ARFCN 871 downlink */
+ trf6151_set_arfcn(arfcn, 0);
+
+ trf6151_set_mode(TRF6151_RX);
+ //trf6151_reg_write(REG_PWR, (PWR_SYNTHE_RX_ON | PWR_RX_MODE | PWR_REGUL_ON | (rf_band<<6) | PWR_BANDGAP_ON));
+ /* Wait for PLL stabilization (170us max) */
+ tpu_enq_wait(TRF6151_RX_PLL_DELAY);
+
+ /* Use DC offset calibration after RX mode has been switched on
+ * (might not be needed) */
+ trf6151_calib_dc_offs();
+
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+}
+
+void trf6151_tx_test(uint16_t arfcn)
+{
+ /* Select ARFCN uplink */
+ trf6151_set_arfcn(arfcn, 1);
+
+ trf6151_set_mode(TRF6151_TX);
+ tpu_enq_wait(TRF6151_RX_PLL_DELAY);
+
+ tpu_enq_sleep();
+ tpu_enable(1);
+ tpu_wait_idle();
+}
+
+#define TRF6151_REGWR_QBITS 8 /* 4 GSM qbits + 4 TPU instructions */
+#define TRF6151_RX_TPU_INSTR 4 /* set_gain(1), set_arfcn(2), set_mode(1) */
+
+/* delay caused by this driver programming the TPU for RX mode */
+#define TRF6151_RX_TPU_DELAY (TRF6151_RX_TPU_INSTR * TRF6151_REGWR_QBITS)
+
+/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */
+void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn)
+{
+ int16_t start_pll_qbits;
+
+ /* power up at the right time _before_ the 'start_qbits' point in time */
+ start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_RX_PLL_DELAY + TRF6151_RX_TPU_DELAY));
+ tpu_enq_at(start_pll_qbits);
+
+ /* Set the AGC and PLL registers */
+ trf6151_set_arfcn(arfcn, 0);
+ trf6151_set_gain(trf6151_vga_dbm, trf6151_gain_high);
+ trf6151_set_mode(TRF6151_RX);
+
+ /* FIXME: power down at the right time again */
+}
+
+/* prepare a Tx window with the TRF6151 finished at time 'start' (in qbits) */
+void trf6151_tx_window(int16_t start_qbits, uint16_t arfcn)
+{
+#ifdef CONFIG_TX_ENABLE
+ int16_t start_pll_qbits;
+
+ /* power up at the right time _before_ the 'start_qbits' point in time */
+ start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_TX_PLL_DELAY + TRF6151_RX_TPU_DELAY));
+ tpu_enq_at(start_pll_qbits);
+
+ trf6151_set_arfcn(arfcn, 1);
+ trf6151_set_mode(TRF6151_TX);
+
+ /* FIXME: power down at the right time again */
+#endif
+}
+
+/* Given the expected input level of exp_inp dBm and the target of target_bb
+ * dBm, configure the RF Frontend with the respective gain */
+void trf6151_compute_gain(int16_t exp_inp, int16_t target_bb)
+{
+ /* TRF6151 VGA gain between 14 to 40 dB, plus 20db high/low */
+ int16_t exp_bb_dbm8, delta_dbm8;
+ int16_t exp_inp_dbm8 = to_dbm8(exp_inp);
+ int16_t target_bb_dbm8 = to_dbm8(target_bb);
+ int16_t vga_gain = TRF6151_GAIN_MIN;
+ int high = 0;
+
+ /* calculate the dBm8 that we expect at the baseband */
+ exp_bb_dbm8 = exp_inp_dbm8 + to_dbm8(system_inherent_gain);
+
+ /* calculate the error that we expect. */
+ delta_dbm8 = target_bb_dbm8 - exp_bb_dbm8;
+
+ /* If this is negative or less than TRF6151_GAIN_MIN, we are pretty
+ * much lost as we cannot reduce the system inherent gain. If it is
+ * positive, it corresponds to the gain that we need to configure */
+ if (delta_dbm8 < to_dbm8(TRF6151_GAIN_MIN)) {
+ printd("AGC Input level overflow\n");
+ high = 0;
+ vga_gain = TRF6151_GAIN_MIN;
+ } else if (delta_dbm8 > to_dbm8(TRF6151_GAIN_FE + TRF6151_GAIN_MIN)) {
+ high = 1;
+ delta_dbm8 -= to_dbm8(TRF6151_GAIN_FE);
+ }
+ vga_gain = delta_dbm8/8;
+ if (vga_gain > TRF6151_GAIN_MAX)
+ vga_gain = TRF6151_GAIN_MAX;
+
+ /* update the static global variables which are used when programming
+ * the window */
+ trf6151_vga_dbm = vga_gain;
+ trf6151_gain_high = high;
+}
diff --git a/src/target_dsp/.gitignore b/src/target_dsp/.gitignore
new file mode 100644
index 00000000..5cf144ea
--- /dev/null
+++ b/src/target_dsp/.gitignore
@@ -0,0 +1,4 @@
+*.o
+*.a
+*.coff
+*.bin
diff --git a/src/target_dsp/calypso/Makefile b/src/target_dsp/calypso/Makefile
new file mode 100644
index 00000000..ff21e694
--- /dev/null
+++ b/src/target_dsp/calypso/Makefile
@@ -0,0 +1,7 @@
+dsp_dump.bin: bl_stage3.S dsp_dump.lds
+ c54x-coff-as bl_stage3.S -o bl_stage3.o
+ c54x-coff-ld --script dsp_dump.lds bl_stage3.o -o dsp_dump.coff
+ c54x-coff-objcopy -j .text -O binary dsp_dump.coff dsp_dump.bin
+
+clean:
+ rm -f *.o *.bin *.coff
diff --git a/src/target_dsp/calypso/bin2cfile.py b/src/target_dsp/calypso/bin2cfile.py
new file mode 100755
index 00000000..51401b8f
--- /dev/null
+++ b/src/target_dsp/calypso/bin2cfile.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+import struct
+import sys
+
+def group_by_n(s, n, do_join=True):
+ return ( ''.join(x) for x in zip(*[s[i::n] for i in range(n)]) )
+
+
+def main(pn, filename):
+ # Get all bytes
+ f = open(filename, 'r')
+ d = f.read()
+ f.close()
+
+ # Get the data
+ ops = ''.join([
+ '0x%04x,%c' % (
+ struct.unpack('=H', x)[0],
+ '\n' if (i&3==3) else ' '
+ )
+ for i, x
+ in enumerate(group_by_n(d, 2))
+ ])[:-1]
+
+ # Header / footer
+ print """
+#define _SA_DECL (const uint16_t *)&(const uint16_t [])
+
+static const struct dsp_section dsp_xxx[] = {
+ {
+ .addr = 0x,
+ .size = 0x%04x,
+ .data = _SA_DECL {
+%s
+ },
+ },
+ { /* Guard */
+ .addr = 0,
+ .size = 0,
+ .data = NULL,
+ },
+};
+
+#undef _SA_DECL
+""" % (len(d)/2, ops)
+
+
+if __name__ == "__main__":
+ main(*sys.argv)
diff --git a/src/target_dsp/calypso/bl_stage3.S b/src/target_dsp/calypso/bl_stage3.S
new file mode 100644
index 00000000..402c3c59
--- /dev/null
+++ b/src/target_dsp/calypso/bl_stage3.S
@@ -0,0 +1,142 @@
+
+BCSR .equ 0x29
+
+CMD_IDLE .equ 1 ; Do nothing / DSP ready for commands
+CMD_COPY_BLOCK .equ 2 ; (if size == 0, then exec)
+CMD_COPY_MODE .equ 4 ; Select copy mode
+ ; (0=code write, 1=data write,
+ ; 2=code read, 3=data read,
+ ; 4=prom read, 5=drom read)
+CMD_VERSION .equ 0xffff ; API_RAM[0] = bootloader version
+
+VERSION .equ 0x0100 ; 1.00
+
+
+ .section .apiram
+
+ .org 0x07fc
+bl_addr_hi .ds 1
+bl_size .ds 1
+bl_addr_lo .ds 1
+bl_status .ds 1
+
+
+ .text
+ .mmregs
+_start:
+ orm #2, *(BCSR) ; ?
+
+ ld #0x1f, DP
+ stm #0x1100, SP
+ stm #0, AR4
+_done:
+ stm #_api_ram, AR2
+ st #CMD_IDLE, @bl_status
+_loop:
+ ; Version
+ cmpm @bl_status, #CMD_VERSION
+ bc 1f, ntc
+
+ bd _done
+ st #VERSION, *AR2
+1:
+
+ ; Select copy mode
+ cmpm @bl_status, #CMD_COPY_MODE
+ bc 1f, ntc
+
+ bd _done
+ mvdm @_api_ram, AR4
+1:
+
+ ; Copy
+ cmpm @bl_status, #CMD_COPY_BLOCK
+ bc _loop, ntc
+
+ ; Capture values for copy operations
+ ; A = full address
+ ; AR1 size-1
+ ; AR2 api_ram (set previously)
+ ; AR3 data/code address
+ ; AR4 mode
+
+ ldu @bl_addr_lo, A
+ stlm A, AR3
+ add @bl_addr_hi, 16, A
+
+ ldu @bl_size, B
+ stlm B, AR1
+ ; mar *AR1- ; We do this in a delay slot later on ...
+
+ ; Start
+ bc 1f, bneq ; B still contains size
+ bacc A
+
+1:
+ ; Select
+ stm #AR4, AR5 ; AR5 = &AR4
+ bit *AR5, 13 ; Test mode(2)
+ bcd _read_rom, tc
+ mar *AR1-
+ bit *AR5, 15 ; Test mode(0) lsb
+ bcd _copy_data, tc
+ bit *AR5, 14 ; Test mode(1)
+ nop
+
+ ; Copy to/from Program space
+_copy_prog:
+ bc _read_prog, tc
+
+ ; Copy from API -> prog space (mode 0)
+_write_prog:
+ rpt *(AR1)
+ writa *AR2+
+ b _done
+
+ ; Copy from prog space -> API (mode 2)
+_read_prog:
+ rpt *(AR1)
+ reada *AR2+
+ b _done
+
+ ; Copy to/from Data space
+_copy_data:
+ bc _read_data, tc
+
+ ; Copy from API -> data space (mode 1)
+_write_data:
+ rpt *(AR1)
+ mvdd *AR2+, *AR3+
+ b _done
+
+ ; Copy from data space -> API (mode 3)
+_read_data:
+ rpt *(AR1)
+ mvdd *AR3+, *AR2+
+ b _done
+
+ ; Read from {D,P}ROM bypassing protection
+_read_rom:
+ ldm AR1, B ; Can't put those two ops in the delay slot of
+ stlm B, BRC ; 'bc' because of unprotected pipeline conflicts
+ bc _read_rom_data, tc
+
+_read_rom_prog:
+ rptb 1f - 1
+ call prom_read_xplt
+1:
+ b _done
+
+_read_rom_data:
+ rptb 1f - 1
+ call drom_read_xplt
+1:
+ b _done
+
+
+drom_read_xplt .equ 0xe4b8
+prom_read_xplt .equ 0x7213
+
+
+ .end
+
diff --git a/src/target_dsp/calypso/dsp_dump.lds b/src/target_dsp/calypso/dsp_dump.lds
new file mode 100644
index 00000000..56633026
--- /dev/null
+++ b/src/target_dsp/calypso/dsp_dump.lds
@@ -0,0 +1,22 @@
+OUTPUT_FORMAT("coff1-c54x")
+OUTPUT_ARCH("")
+MEMORY
+{
+ apiram (RWXI) : ORIGIN = 0x0800, LENGTH = 0x2000
+}
+SECTIONS
+{
+ . = 0x0800;
+
+ .apiram :
+ {
+ PROVIDE(_api_ram = .);
+ *(.apiram)
+ } > apiram
+
+ .text :
+ {
+ *(.text)
+ } > apiram
+}
+
diff --git a/src/target_dsp/calypso/dump2coff.py b/src/target_dsp/calypso/dump2coff.py
new file mode 100755
index 00000000..67a49e8e
--- /dev/null
+++ b/src/target_dsp/calypso/dump2coff.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python
+
+from collections import namedtuple
+import re
+import sys
+import struct
+
+DATA = 0
+DATA = 1
+
+
+class Section(object):
+
+ DATA = 0
+ CODE = 1
+
+ STYP_NOLOAD = 0x0002
+ STYP_TEXT = 0x0020
+ STYP_DATA = 0x0040
+ STYP_BSS = 0x0080
+
+ def __init__(self, name, type, start, size, data=None):
+ self.name = name
+ self.type = type
+ self.start = start
+ self.size = size
+ self.data = data
+
+ @property
+ def flags(self):
+ if self.type == Section.DATA:
+ return Section.STYP_DATA if self.data else Section.STYP_BSS
+ else:
+ return Section.STYP_TEXT if self.data else Section.STYP_NOLOAD
+
+
+class CalypsoCOFF(object):
+
+ F_RELFLG = 0x0001 # Relocation information stripped from the file
+ F_EXEC = 0x0002 # File is executable (i.e., no unresolved external references)
+ F_LNNO = 0x0004 # Line numbers stripped from the file
+ F_LSYMS = 0x0010 # Local symbols stripped from the file
+ F_LITTLE = 0x0100 # Little endian
+
+ def __init__(self, data_seg_base=0x80000000):
+ self.sections = {}
+ self.data_seg_base = data_seg_base
+ self.ver_magic = 0x00c1
+ self.tgt_magic = 0x0098
+ self.flags = \
+ CalypsoCOFF.F_RELFLG | \
+ CalypsoCOFF.F_EXEC | \
+ CalypsoCOFF.F_LNNO | \
+ CalypsoCOFF.F_LSYMS | \
+ CalypsoCOFF.F_LITTLE
+
+ def _data_pack(self, d):
+ return ''.join(struct.pack('<H', x) for x in d)
+
+ def save(self, filename):
+ # Formats
+ HDR_FILE = '<HHlllHHH'
+ HDR_SECTIONS = '<8sLLllllHHHcc'
+
+ # Optional header
+ oh = ''
+
+ # File header
+ fh = struct.pack(HDR_FILE,
+ self.ver_magic, # unsigned short f_ver_magic; /* version magic number */
+ len(self.sections), # unsigned short f_nscns; /* number of section */
+ 0, # long f_timdat; /* time and date stamp */
+ 0, # long f_symptr; /* file ptr to symbol table */
+ 0, # long f_nsyms; /* number entries in the sym table */
+ len(oh), # unsigned short f_opthdr; /* size of optional header */
+ self.flags, # unsigned short f_flags; /* flags */
+ self.tgt_magic, # unsigned short f_tgt_magic; /* target magic number */
+ )
+
+ # File header size + #sections * sizeof(section header)
+ dptr = struct.calcsize(HDR_FILE) + len(oh) + len(self.sections) * struct.calcsize(HDR_SECTIONS)
+
+ # Section headers
+ sh = []
+ sd = []
+
+ sk = lambda x: self.data_seg_base + x.start if x.type==Section.DATA else x.start
+
+ for s in sorted(self.sections.values(), key=sk):
+ # Values
+ if s.type == Section.DATA:
+ mp = 0x80
+ sa = s.start
+ else:
+ mp = 0
+ sa = s.start
+ sptr = dptr if s.data else 0
+
+ # Header
+ sh.append(struct.pack(HDR_SECTIONS,
+ s.name, # char[8] s_name; /* 8-character null padded section name */
+ sa, # long int s_paddr; /* Physical address of section */
+ sa, # long int s_vaddr; /* Virtual address of section */
+ s.size, # long int s_size; /* Section size in bytes */
+ sptr, # long int s_scnptr; /* File pointer to raw data */
+ 0, # long int s_relptr; /* File pointer to relocation entries */
+ 0, # long int s_lnnoptr;/* File pointer to line number entries */
+ 0, # unsigned short s_nreloc; /* Number of relocation entrie */
+ 0, # unsigned short s_nlnno; /* Number of line number entries */
+ s.flags,# unsigned short s_flags; /* Flags (see ``Section header flags'') */
+ '\x00', # /
+ chr(mp),# char s_mempage;/* Memory page number */
+ ))
+
+ # Data
+ if s.data:
+ sd.append(self._data_pack(s.data))
+ dptr += s.size * 2
+
+ # Write the thing
+ f = open(filename, 'wb')
+
+ f.write(fh)
+ f.write(oh)
+ f.write(''.join(sh))
+ f.write(''.join(sd))
+
+ f.close()
+
+ def add_section(self, name, type, addr, size, data=None):
+ self.sections[name] = Section(name, type, addr, size, data=data)
+
+
+# ----------------------------------------------------------------------------
+# Dump loading
+# ----------------------------------------------------------------------------
+
+RE_DUMP_HDR = re.compile(
+ r"^DSP dump: (\w*) \[([0-9a-fA-F]{5})-([0-9a-fA-F]{5})\]$"
+)
+
+
+def _file_strip_gen(f):
+ while True:
+ l = f.readline()
+ if not l:
+ return
+ yield l.strip()
+
+
+def dump_load_section(fg, sa, ea):
+ data = []
+ ca = sa
+ for l in fg:
+ if not l:
+ break
+
+ ra = int(l[0:5], 16)
+ if ra != ca:
+ raise ValueError('Invalid dump address %05x != %05x', ra, ca)
+
+ v = l[8:].split()
+ if len(v) != 16:
+ raise ValueError('Invalid dump format')
+
+ v = [int(x,16) for x in v]
+ data.extend(v)
+
+ ca += 0x10
+
+ if ca != ea:
+ raise ValueError('Missing dump data %05x != %05x', ra, ea)
+
+ return data
+
+
+def dump_load(filename):
+ # Open file
+ f = open(filename, 'r')
+ fg = _file_strip_gen(f)
+
+ # Scan line by line for a dump header line
+ sections = []
+
+ for l in fg:
+ m = RE_DUMP_HDR.match(l)
+ if not m:
+ continue
+
+ name = m.group(1)
+ sa = int(m.group(2), 16)
+ ea = int(m.group(3), 16) + 1
+
+ sections.append((
+ name, sa, ea,
+ dump_load_section(fg, sa, ea),
+ ))
+
+ # Done
+ f.close()
+
+ return sections
+
+
+# ----------------------------------------------------------------------------
+# Main
+# ----------------------------------------------------------------------------
+
+def main(pname, dump_filename, out_filename):
+
+ # Section to place in the COFF
+ sections = [
+ # name type start size
+ ('.regs', Section.DATA, 0x00000, 0x0060),
+ ('.scratch', Section.DATA, 0x00060, 0x0020),
+ ('.drom', Section.DATA, 0x09000, 0x5000),
+ ('.pdrom', Section.CODE, 0x0e000, 0x2000),
+ ('.prom0', Section.CODE, 0x07000, 0x7000),
+ ('.prom1', Section.CODE, 0x18000, 0x8000),
+ ('.prom2', Section.CODE, 0x28000, 0x8000),
+ ('.prom3', Section.CODE, 0x38000, 0x2000),
+ ('.daram0', Section.DATA, 0x00080, 0x0780),
+ ('.api', Section.DATA, 0x00800, 0x2000),
+ ('.daram1', Section.DATA, 0x02800, 0x4800),
+ ]
+
+ # COFF name -> dump name
+ dump_mapping = {
+ # '.regs' : 'Registers',
+ '.drom' : 'DROM',
+ '.pdrom' : 'PDROM',
+ '.prom0' : 'PROM0',
+ '.prom1' : 'PROM1',
+ '.prom2' : 'PROM2',
+ '.prom3' : 'PROM3',
+ }
+
+ # Load the dump
+ dump_sections = dict([(s[0], s) for s in dump_load(dump_filename)])
+
+ # Create the COFF
+ coff = CalypsoCOFF()
+
+ # Add each section (with data if we have some)
+ for name, type, start, size in sections:
+ # Dumped data ?
+ d_data = None
+ if (name in dump_mapping) and (dump_mapping[name] in dump_sections):
+ d_name, d_sa, d_ea, d_data = dump_sections[dump_mapping[name]]
+
+ # Add sections
+ coff.add_section(name, type, start, size, d_data)
+
+ # Save result
+ coff.save(out_filename)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(*sys.argv))
diff --git a/src/target_dsp/calypso/ida/README.txt b/src/target_dsp/calypso/ida/README.txt
new file mode 100644
index 00000000..a7939083
--- /dev/null
+++ b/src/target_dsp/calypso/ida/README.txt
@@ -0,0 +1,73 @@
+Here's a few steps to get started quickly and get something readable:
+
+ - Compile a patched for the IDA TMS320C54 module
+
+ I made several enhancement to it to support the calypso better (the tms320c54
+ module is part of the SDK and can be modded and recompiled) :
+
+ - Add support for memory mappings so that the same memory zone can
+ 'appear' at several place in the address space (to handle data & code
+ overlay)
+ - Fix the section handling when loading a file:
+ . to set XPC properly,
+ . to not override section name
+ . to support more than 2 sections
+ - Fix a bug in cross reference detection when dealing with section
+ having selectors != 0
+ - Add stub support for the type system. This allows loading of a .h
+ header file with the NDB structure definition
+ - Add definition for the IO ports so that they are symbolically
+ displayed
+
+ I can't publically distribute the IDA processor module modification
+ because even just the patch contains some hex-rays code, so I'll handle
+ this on a case by case basis. (just ask me privately and we'll work it out)
+
+ - Dump the DSP ROM
+
+ Using the compal_dsp_dump.bin, you must create a text dump of the DSP ROM,
+ just piping the console output to a text file.
+
+ - Generate COFF image
+
+ The dump2coff.py script can convert the text dump into a usable COFF file
+ containing all the correct sections and addresses.
+
+ - Load this COFF image into IDA
+
+ In the load dialog make sure :
+ - Uncheck the 'Fill segment gaps (COFF)' checkbox
+ - Select 'TMS320C54' in 'Change processor'
+ - In 'Analysis Options/Processor specific analysis options' :
+ - 'Choose device name': CALYPSO
+ - 'Data segment address': 0x80000000
+ - 'Add mapping' (do it several time)
+ - From 0x00000060 -> 0x80000060 size 0x6FA0
+ - From 0x00010060 -> 0x80000060 size 0x6FA0
+ - From 0x00020060 -> 0x80000060 size 0x6FA0
+ - From 0x00030060 -> 0x80000060 size 0x6FA0
+ - From 0x8000E000 -> 0x0000E000 size 0x2000
+
+ - Set 'stub' compiler options to allow the type system to load .h files
+
+ In 'Options/Compiler':
+ - Compiler: 'GNU C++'
+ - Calling convention: 'Cdecl'
+ - Memory model: 'Code Near, Data Near'
+ - Pointer size: 'Near 16bit, Far 32bit'
+ - Include directory: '/usr/include' (or a directory with your includes
+ ... needs to exist)
+
+ - Load the NDB types
+
+ - Load the ndb.h file
+ - In the local types view, import all structure / enum into the database
+ - Then declare the following symbol and set them as struct type
+ appropriately.
+
+ 0x80000800 api_w_page_0 db_mcu_to_dsp
+ 0x80000814 api_w_page_1 db_mcu_to_dsp
+ 0x80000828 api_r_page_0 db_dsp_to_mcu
+ 0x8000083c api_r_page_1 db_dsp_to_mcu
+ 0x800008d4 ndb ndb_mcu_dsp
+
diff --git a/src/target_dsp/calypso/ida/ndb.h b/src/target_dsp/calypso/ida/ndb.h
new file mode 100644
index 00000000..ad9c1056
--- /dev/null
+++ b/src/target_dsp/calypso/ida/ndb.h
@@ -0,0 +1,294 @@
+typedef unsigned char API;
+typedef signed char API_SIGNED;
+
+struct db_mcu_to_dsp
+{
+ API d_task_d;
+ API d_burst_d;
+ API d_task_u;
+ API d_burst_u;
+ API d_task_md;
+ API d_background;
+ API d_debug;
+ API d_task_ra;
+ API d_fn;
+ API d_ctrl_tch;
+ API hole;
+ API d_ctrl_abb;
+ API a_a5fn[2];
+ API d_power_ctl;
+ API d_afc;
+ API d_ctrl_system;
+};
+
+struct db_dsp_to_mcu
+{
+ API d_task_d;
+ API d_burst_d;
+ API d_task_u;
+ API d_burst_u;
+ API d_task_md;
+ API d_background;
+ API d_debug;
+ API d_task_ra;
+ API a_serv_demod[4];
+ API a_pm[3];
+ API a_sch[5];
+};
+
+struct param_mcu_dsp
+{
+ API_SIGNED d_transfer_rate;
+ API_SIGNED d_lat_mcu_bridge;
+ API_SIGNED d_lat_mcu_hom2sam;
+ API_SIGNED d_lat_mcu_bef_fast_access;
+ API_SIGNED d_lat_dsp_after_sam;
+ API_SIGNED d_gprs_install_address;
+ API_SIGNED d_misc_config;
+ API_SIGNED d_cn_sw_workaround;
+ API_SIGNED d_hole2_param[4];
+ API_SIGNED d_fb_margin_beg;
+ API_SIGNED d_fb_margin_end;
+ API_SIGNED d_nsubb_idle;
+ API_SIGNED d_nsubb_dedic;
+ API_SIGNED d_fb_thr_det_iacq;
+ API_SIGNED d_fb_thr_det_track;
+ API_SIGNED d_dc_off_thres;
+ API_SIGNED d_dummy_thres;
+ API_SIGNED d_dem_pond_gewl;
+ API_SIGNED d_dem_pond_red;
+ API_SIGNED d_maccthresh1;
+ API_SIGNED d_mldt;
+ API_SIGNED d_maccthresh;
+ API_SIGNED d_gu;
+ API_SIGNED d_go;
+ API_SIGNED d_attmax;
+ API_SIGNED d_sm;
+ API_SIGNED d_b;
+ API_SIGNED d_v42b_switch_hyst;
+ API_SIGNED d_v42b_switch_min;
+ API_SIGNED d_v42b_switch_max;
+ API_SIGNED d_v42b_reset_delay;
+ API_SIGNED d_ldT_hr;
+ API_SIGNED d_maccthresh_hr;
+ API_SIGNED d_maccthresh1_hr;
+ API_SIGNED d_gu_hr;
+ API_SIGNED d_go_hr;
+ API_SIGNED d_b_hr;
+ API_SIGNED d_sm_hr;
+ API_SIGNED d_attmax_hr;
+ API_SIGNED c_mldt_efr;
+ API_SIGNED c_maccthresh_efr;
+ API_SIGNED c_maccthresh1_efr;
+ API_SIGNED c_gu_efr;
+ API_SIGNED c_go_efr;
+ API_SIGNED c_b_efr;
+ API_SIGNED c_sm_efr;
+ API_SIGNED c_attmax_efr;
+ API_SIGNED d_sd_min_thr_tchfs;
+ API_SIGNED d_ma_min_thr_tchfs;
+ API_SIGNED d_md_max_thr_tchfs;
+ API_SIGNED d_md1_max_thr_tchfs;
+ API_SIGNED d_sd_min_thr_tchhs;
+ API_SIGNED d_ma_min_thr_tchhs;
+ API_SIGNED d_sd_av_thr_tchhs;
+ API_SIGNED d_md_max_thr_tchhs;
+ API_SIGNED d_md1_max_thr_tchhs;
+ API_SIGNED d_sd_min_thr_tchefs;
+ API_SIGNED d_ma_min_thr_tchefs;
+ API_SIGNED d_md_max_thr_tchefs;
+ API_SIGNED d_md1_max_thr_tchefs;
+ API_SIGNED d_wed_fil_ini;
+ API_SIGNED d_wed_fil_tc;
+ API_SIGNED d_x_min;
+ API_SIGNED d_x_max;
+ API_SIGNED d_slope;
+ API_SIGNED d_y_min;
+ API_SIGNED d_y_max;
+ API_SIGNED d_wed_diff_threshold;
+ API_SIGNED d_mabfi_min_thr_tchhs;
+ API_SIGNED d_facch_thr;
+ API_SIGNED d_max_ovsp_ul;
+ API_SIGNED d_sync_thres;
+ API_SIGNED d_idle_thres;
+ API_SIGNED d_m1_thres;
+ API_SIGNED d_max_ovsp_dl;
+ API_SIGNED d_gsm_bgd_mgt;
+ API a_fir_holes[4];
+ API a_fir31_uplink[31];
+ API a_fir31_downlink[31];
+};
+
+struct ndb_mcu_dsp
+{
+ API d_dsp_page;
+ API d_error_status;
+ API d_spcx_rif;
+ API d_tch_mode;
+ API d_debug1;
+ API d_dsp_test;
+ API d_version_number1;
+ API d_version_number2;
+ API d_debug_ptr;
+ API d_debug_bk;
+ API d_pll_config;
+ API p_debug_buffer;
+ API d_debug_buffer_size;
+ API d_debug_trace_type;
+ API d_dsp_state;
+ API d_hole1_ndb[2];
+ API d_hole_debug_amr;
+ API d_hole2_ndb[1];
+ API d_mcsi_select;
+ API d_apcdel1_bis;
+ API d_apcdel2_bis;
+ API d_apcdel2;
+ API d_vbctrl2;
+ API d_bulgcal;
+ API d_afcctladd;
+ API d_vbuctrl;
+ API d_vbdctrl;
+ API d_apcdel1;
+ API d_apcoff;
+ API d_bulioff;
+ API d_bulqoff;
+ API d_dai_onoff;
+ API d_auxdac;
+ API d_vbctrl1;
+ API d_bbctrl;
+ API d_fb_det;
+ API d_fb_mode;
+ API a_sync_demod[4];
+ API a_sch26[5];
+ API d_audio_gain_ul;
+ API d_audio_gain_dl;
+ API d_audio_compressor_ctrl;
+ API d_audio_init;
+ API d_audio_status;
+ API d_toneskb_init;
+ API d_toneskb_status;
+ API d_k_x1_t0;
+ API d_k_x1_t1;
+ API d_k_x1_t2;
+ API d_pe_rep;
+ API d_pe_off;
+ API d_se_off;
+ API d_bu_off;
+ API d_t0_on;
+ API d_t0_off;
+ API d_t1_on;
+ API d_t1_off;
+ API d_t2_on;
+ API d_t2_off;
+ API d_k_x1_kt0;
+ API d_k_x1_kt1;
+ API d_dur_kb;
+ API d_shiftdl;
+ API d_shiftul;
+ API d_aec_ctrl;
+ API d_es_level_api;
+ API d_mu_api;
+ API d_melo_osc_used;
+ API d_melo_osc_active;
+ API a_melo_note0[4];
+ API a_melo_note1[4];
+ API a_melo_note2[4];
+ API a_melo_note3[4];
+ API a_melo_note4[4];
+ API a_melo_note5[4];
+ API a_melo_note6[4];
+ API a_melo_note7[4];
+ API d_melody_selection;
+ API a_melo_holes[3];
+ API d_sr_status;
+ API d_sr_param;
+ API d_sr_bit_exact_test;
+ API d_sr_nb_words;
+ API d_sr_db_level;
+ API d_sr_db_noise;
+ API d_sr_mod_size;
+ API a_n_best_words[4];
+ API a_n_best_score[8];
+ API a_dd_1[22];
+ API a_du_1[22];
+ API d_v42b_nego0;
+ API d_v42b_nego1;
+ API d_v42b_control;
+ API d_v42b_ratio_ind;
+ API d_mcu_control;
+ API d_mcu_control_sema;
+ API d_background_enable;
+ API d_background_abort;
+ API d_background_state;
+ API d_max_background;
+ API a_background_tasks[16];
+ API a_back_task_io[16];
+ API d_gea_mode_ovly;
+ API a_gea_kc_ovly[4];
+ API d_hole3_ndb[7];
+ API d_thr_usf_detect;
+ API d_a5mode;
+ API d_sched_mode_gprs_ovly;
+ API d_hole4_ndb[5];
+ API a_ramp[16];
+ API a_cd[15];
+ API a_fd[15];
+ API a_dd_0[22];
+ API a_cu[15];
+ API a_fu[15];
+ API a_du_0[22];
+ API d_rach;
+ API a_kc[4];
+ API d_ra_conf;
+ API d_ra_act;
+ API d_ra_test;
+ API d_ra_statu;
+ API d_ra_statd;
+ API d_fax;
+ API a_data_buf_ul[21];
+ API a_data_buf_dl[37];
+ API a_tty_holes[8];
+ API a_sr_holes0[414];
+ API a_new_aec_holes[12];
+ // API a_sr_holes1[145];
+ struct param_mcu_dsp params;
+ API d_cport_init;
+ API d_cport_ctrl;
+ API a_cport_cfr[2];
+ API d_cport_tcl_tadt;
+ API d_cport_tdat;
+ API d_cport_tvs;
+ API d_cport_status;
+ API d_cport_reg_value;
+ API a_cport_holes[1011];
+ API a_model[1041];
+ API a_eotd_holes[22];
+ API a_amr_config[4];
+ API a_ratscch_ul[6];
+ API a_ratscch_dl[6];
+ API d_amr_snr_est;
+ API a_voice_memo_amr_holes[1];
+ API d_thr_onset_afs;
+ API d_thr_sid_first_afs;
+ API d_thr_ratscch_afs;
+ API d_thr_update_afs;
+ API d_thr_onset_ahs;
+ API d_thr_sid_ahs;
+ API d_thr_ratscch_marker;
+ API d_thr_sp_dgr;
+ API d_thr_soft_bits;
+ API d_holes[61];
+};
+
+enum dsp_error {
+ DSP_ERR_RHEA = 0x0001,
+ DSP_ERR_IQ_SAMPLES = 0x0004,
+ DSP_ERR_DMA_PROG = 0x0008,
+ DSP_ERR_DMA_TASK = 0x0010,
+ DSP_ERR_DMA_PEND = 0x0020,
+ DSP_ERR_VM = 0x0080,
+ DSP_ERR_DMA_UL_TASK = 0x0100,
+ DSP_ERR_DMA_UL_PROG = 0x0200,
+ DSP_ERR_DMA_UL_PEND = 0x0400,
+ DSP_ERR_STACK_OV = 0x0800,
+};
diff --git a/src/target_dsp/calypso/ida/tms320c54.cfg b/src/target_dsp/calypso/ida/tms320c54.cfg
new file mode 100644
index 00000000..7962bee2
--- /dev/null
+++ b/src/target_dsp/calypso/ida/tms320c54.cfg
@@ -0,0 +1,136 @@
+; Append this to the tms320c54.cfg shipped with IDA
+
+.CALYPSO
+
+; entry _reset 0xff80 Reset vector
+
+; RIF
+RIF_DXR 0x0000
+RIF_DRR 0x0001
+RIF_SPCX 0x0002
+RIF_SPCR 0x0003
+
+; CYPHER
+CYPHER_CNTL 0x2800
+CYPHER_CNTL.START 0
+CYPHER_CNTL.RESETSW 1
+CYPHER_CNTL.MODE0 2
+CYPHER_CNTL.MODE1 3
+CYPHER_CNTL.CLK_EN 4
+CYPHER_CNTL.CYPHER_ONLY 5
+
+CYPHER_STATUS_IRQ 0x2801
+CYPHER_STATUS_IRQ.LT_FIN 0
+
+CYPHER_STATUS_WORK 0x2802
+CYPHER_STATUS_WORK.WORKING 0
+
+CYPHER_KC_1 0x2803
+CYPHER_KC_2 0x2804
+CYPHER_KC_3 0x2805
+CYPHER_KC_4 0x2806
+CYPHER_COUNT_1 0x2807
+CYPHER_COUNT_2 0x2808
+CYPHER_DECI_1 0x2809
+CYPHER_DECI_2 0x280A
+CYPHER_DECI_3 0x280B
+CYPHER_DECI_4 0x280C
+CYPHER_DECI_5 0x280D
+CYPHER_DECI_6 0x280E
+CYPHER_DECI_7 0x280F
+CYPHER_DECI_8 0x2810
+CYPHER_ENCI_1 0x2811
+CYPHER_ENCI_2 0x2812
+CYPHER_ENCI_3 0x2813
+CYPHER_ENCI_4 0x2814
+CYPHER_ENCI_5 0x2815
+CYPHER_ENCI_6 0x2816
+CYPHER_ENCI_7 0x2817
+CYPHER_ENCI_8 0x2818
+
+; MCSI
+MCSI_CONTROL 0x0800
+MCSI_MAIN-PARAMETERS 0x0801
+MCSI_INTERRUPTS 0x0802
+MCSI_CHANNEL-USED 0x0803
+MCSI_OVER-CLK 0x0804
+MCSI_CLK-FREQ 0x0805
+MCSI_STATUS 0x0806
+MCSI_TX0 0x0820
+MCSI_TX1 0x0821
+MCSI_TX2 0x0822
+MCSI_TX3 0x0823
+MCSI_TX4 0x0824
+MCSI_TX5 0x0825
+MCSI_TX6 0x0826
+MCSI_TX7 0x0827
+MCSI_TX8 0x0828
+MCSI_TX9 0x0829
+MCSI_TX10 0x082A
+MCSI_TX11 0x082B
+MCSI_TX12 0x082C
+MCSI_TX13 0x082D
+MCSI_TX14 0x082E
+MCSI_TX15 0x082F
+MCSI_RX0 0x0830
+MCSI_RX1 0x0831
+MCSI_RX2 0x0832
+MCSI_RX3 0x0833
+MCSI_RX4 0x0834
+MCSI_RX5 0x0835
+MCSI_RX6 0x0836
+MCSI_RX7 0x0837
+MCSI_RX8 0x0838
+MCSI_RX9 0x0839
+MCSI_RX10 0x083A
+MCSI_RX11 0x083B
+MCSI_RX12 0x083C
+MCSI_RX13 0x083D
+MCSI_RX14 0x083E
+MCSI_RX15 0x083F
+
+; RHEA
+RHEA_TRANSFER_RATE 0xF800
+
+RHEA_BRIDGE-CTRL 0xF801
+RHEA_BRIDGE-CTRL.TIMEOUT_ENABLE 8
+RHEA_BRIDGE-CTRL.NSUPV 9
+
+; API
+API_CONF 0xF900
+API_CONF.RESERVED0 0
+API_CONF.API_HOM 1
+API_CONF.BRIDGE_CLK_EN 2
+
+; Interrupts
+INT_CNTRL 0xFA00
+INT_CLEAR 0xFA01
+
+; DMA
+DMA_CONTROLLER_CONF 0xFC00
+DMA_ALLOC_CONFIG 0xFC02
+DMA1_RAD 0xFC10
+DMA1_RDPTH 0xFC12
+DMA1_AAD 0xFC14
+DMA1_ALGTH 0xFC16
+DMA1_CTRL 0xFC18
+DMA1_CUR_OFFSET_API 0xFC1A
+DMA2_RAD 0xFC20
+DMA2_RDPTH 0xFC22
+DMA2_AAD 0xFC24
+DMA2_ALGTH 0xFC26
+DMA2_CTRL 0xFC28
+DMA2_CUR_OFFSET_API 0xFC2A
+DMA3_RAD 0xFC30
+DMA3_RDPTH 0xFC32
+DMA3_AAD 0xFC34
+DMA3_ALGTH 0xFC36
+DMA3_CTRL 0xFC38
+DMA3_CUR_OFFSET_API 0xFC3A
+DMA4_RAD 0xFC40
+DMA4_RDPTH 0xFC42
+DMA4_AAD 0xFC44
+DMA4_ALGTH 0xFC46
+DMA4_CTRL 0xFC48
+DMA4_CUR_OFFSET_API 0xFC4A
+
diff --git a/src/wireshark/gsmtap.patch b/src/wireshark/gsmtap.patch
new file mode 100644
index 00000000..019c8b49
--- /dev/null
+++ b/src/wireshark/gsmtap.patch
@@ -0,0 +1,445 @@
+Index: epan/dissectors/packet-gsmtap.c
+===================================================================
+--- /dev/null
++++ epan/dissectors/packet-gsmtap.c
+@@ -0,0 +1,345 @@
++/* packet-gsmtap.c
++ * Routines for GSMTAP captures
++ *
++ * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
++ *
++ * $Id$
++ *
++ * Wireshark - Network traffic analyzer
++ * By Gerald Combs <gerald@wireshark.org>
++ * Copyright 1998 Gerald Combs
++ *
++ * 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., 59 Temple Place - Suite 330, Boston, MA
++ * 02111-1307, USA.
++ *
++ */
++
++/* GSMTAP is a generic header format for GSM protocol captures,
++ * it uses the IANA-assigned UDP port number 4729 and carries
++ * payload in various formats of GSM interfaces such as Um MAC
++ * blocks or Um bursts.
++ *
++ * Example programs generating GSMTAP data are airprobe
++ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/)
++ */
++
++#ifdef HAVE_CONFIG_H
++# include "config.h"
++#endif
++
++#include <glib.h>
++#include <epan/packet.h>
++#include <epan/prefs.h>
++
++#include "packet-gsmtap.h"
++
++static int proto_gsmtap = -1;
++
++static int hf_gsmtap_version = -1;
++static int hf_gsmtap_hdrlen = -1;
++static int hf_gsmtap_type = -1;
++static int hf_gsmtap_timeslot = -1;
++static int hf_gsmtap_subslot = -1;
++static int hf_gsmtap_arfcn = -1;
++static int hf_gsmtap_uplink = -1;
++static int hf_gsmtap_noise_dbm = -1;
++static int hf_gsmtap_signal_dbm = -1;
++static int hf_gsmtap_frame_nr = -1;
++static int hf_gsmtap_burst_type = -1;
++static int hf_gsmtap_channel_type = -1;
++static int hf_gsmtap_antenna = -1;
++
++static int hf_sacch_l1h_power_lev = -1;
++static int hf_sacch_l1h_fpc = -1;
++static int hf_sacch_l1h_ta = -1;
++
++static gint ett_gsmtap = -1;
++
++enum {
++ GSMTAP_SUB_DATA = 0,
++ GSMTAP_SUB_UM,
++ GSMTAP_SUB_UM_LAPDM,
++ GSMTAP_SUB_ABIS,
++
++ GSMTAP_SUB_MAX
++};
++
++static dissector_handle_t sub_handles[GSMTAP_SUB_MAX];
++
++static const value_string gsmtap_bursts[] = {
++ { GSMTAP_BURST_UNKNOWN, "UNKNOWN" },
++ { GSMTAP_BURST_FCCH, "FCCH" },
++ { GSMTAP_BURST_PARTIAL_SCH, "PARTIAL SCH" },
++ { GSMTAP_BURST_SCH, "SCH" },
++ { GSMTAP_BURST_CTS_SCH, "CTS SCH" },
++ { GSMTAP_BURST_COMPACT_SCH, "COMPACT SCH" },
++ { GSMTAP_BURST_NORMAL, "NORMAL" },
++ { GSMTAP_BURST_DUMMY, "DUMMY" },
++ { GSMTAP_BURST_ACCESS, "RACH" },
++ { 0, NULL },
++};
++
++static const value_string gsmtap_channels[] = {
++ { GSMTAP_CHANNEL_UNKNOWN, "UNKNOWN" },
++ { GSMTAP_CHANNEL_BCCH, "BCCH" },
++ { GSMTAP_CHANNEL_CCCH, "CCCH" },
++ { GSMTAP_CHANNEL_RACH, "RACH" },
++ { GSMTAP_CHANNEL_AGCH, "AGCH" },
++ { GSMTAP_CHANNEL_PCH, "PCH" },
++ { GSMTAP_CHANNEL_SDCCH, "SDCCH" },
++ { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" },
++ { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" },
++ { GSMTAP_CHANNEL_TCH_F, "FACCH/F" },
++ { GSMTAP_CHANNEL_TCH_H, "FACCH/H" },
++ { GSMTAP_CHANNEL_ACCH|
++ GSMTAP_CHANNEL_SDCCH, "LSACCH" },
++ { GSMTAP_CHANNEL_ACCH|
++ GSMTAP_CHANNEL_SDCCH4, "SACCH/4" },
++ { GSMTAP_CHANNEL_ACCH|
++ GSMTAP_CHANNEL_SDCCH8, "SACCH/8" },
++ { GSMTAP_CHANNEL_ACCH|
++ GSMTAP_CHANNEL_TCH_F, "SACCH/F" },
++ { GSMTAP_CHANNEL_ACCH|
++ GSMTAP_CHANNEL_TCH_F, "SACCH/H" },
++ { 0, NULL },
++};
++
++static const value_string gsmtap_types[] = {
++ { GSMTAP_TYPE_UM, "GSM Um (MS<->BTS)" },
++ { GSMTAP_TYPE_ABIS, "GSM Abis (BTS<->BSC)" },
++ { GSMTAP_TYPE_UM_BURST, "GSM Um burst (MS<->BTS)" },
++ { 0, NULL },
++};
++
++/* dissect a SACCH L1 header which is included in the first 2 bytes
++ * of every SACCH frame (according to TS 04.04) */
++static void
++dissect_sacch_l1h(tvbuff_t *tvb, proto_tree *tree)
++{
++ proto_item *ti;
++ proto_tree *l1h_tree = NULL;
++
++ if (!tree)
++ return;
++
++ ti = proto_tree_add_protocol_format(tree, proto_gsmtap, tvb, 0, 2,
++ "SACCH L1 Header, Power Level: %u, Timing Advance: %u",
++ tvb_get_guint8(tvb, 0) & 0x1f,
++ tvb_get_guint8(tvb, 1));
++ l1h_tree = proto_item_add_subtree(ti, ett_gsmtap);
++ /* Power Level */
++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_power_lev, tvb, 0, 1, FALSE);
++ /* Fast Power Control */
++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_fpc, tvb, 0, 1, FALSE);
++ /* Acutal Timing Advance */
++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_ta, tvb, 1, 1, FALSE);
++}
++
++/* dissect a GSMTAP header and hand payload off to respective dissector */
++static void
++dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
++{
++ int sub_handle, len, offset = 0;
++ proto_item *ti;
++ proto_tree *gsmtap_tree = NULL;
++ tvbuff_t *payload_tvb, *l1h_tvb = NULL;
++ guint8 hdr_len, type, sub_type;
++ guint16 arfcn;
++
++ len = tvb_length(tvb);
++
++ hdr_len = tvb_get_guint8(tvb, offset + 1) <<2;
++ type = tvb_get_guint8(tvb, offset + 2);
++ sub_type = tvb_get_guint8(tvb, offset + 12);
++ arfcn = tvb_get_ntohs(tvb, offset + 4);
++
++ /* In case of a SACCH, there is a two-byte L1 header in front
++ * of the packet (see TS 04.04) */
++ if (type == GSMTAP_TYPE_UM &&
++ sub_type & GSMTAP_CHANNEL_ACCH) {
++ l1h_tvb = tvb_new_subset(tvb, hdr_len, 2, 2);
++ payload_tvb = tvb_new_subset(tvb, hdr_len+2, len-(hdr_len+2),
++ len-(hdr_len+2));
++ } else {
++ payload_tvb = tvb_new_subset(tvb, hdr_len, len-hdr_len,
++ len-hdr_len);
++ }
++
++ /* We don't want any UDP related info left in the INFO field, as the
++ * gsm_a_dtap dissector will not clear but only append */
++ col_clear(pinfo->cinfo, COL_INFO);
++
++ col_set_str(pinfo->cinfo, COL_PROTOCOL, "GSMTAP");
++
++ if (arfcn & GSMTAP_ARFCN_F_UPLINK) {
++ col_append_str(pinfo->cinfo, COL_RES_NET_SRC, "MS");
++ col_append_str(pinfo->cinfo, COL_RES_NET_DST, "BTS");
++ /* p2p_dir is used by the LAPDm dissector */
++ pinfo->p2p_dir = P2P_DIR_SENT;
++ } else {
++ col_set_str(pinfo->cinfo, COL_RES_NET_SRC, "BTS");
++ switch (sub_type & ~GSMTAP_CHANNEL_ACCH) {
++ case GSMTAP_CHANNEL_BCCH:
++ case GSMTAP_CHANNEL_CCCH:
++ case GSMTAP_CHANNEL_PCH:
++ case GSMTAP_CHANNEL_AGCH:
++ col_set_str(pinfo->cinfo, COL_RES_NET_DST, "Broadcast");
++ break;
++ default:
++ col_set_str(pinfo->cinfo, COL_RES_NET_DST, "MS");
++ break;
++ }
++ /* p2p_dir is used by the LAPDm dissector */
++ pinfo->p2p_dir = P2P_DIR_RECV;
++ }
++
++ if (tree) {
++ ti = proto_tree_add_protocol_format(tree, proto_gsmtap, tvb, 0, hdr_len,
++ "GSM TAP Header, ARFCN: %u (%s), TS: %u, Channel: %s (%u)",
++ arfcn & GSMTAP_ARFCN_MASK,
++ arfcn & GSMTAP_ARFCN_F_UPLINK ? "Uplink" : "Downlink",
++ tvb_get_guint8(tvb, offset+3),
++ match_strval(tvb_get_guint8(tvb, offset+12), gsmtap_channels),
++ tvb_get_guint8(tvb, offset+14));
++ gsmtap_tree = proto_item_add_subtree(ti, ett_gsmtap);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_version,
++ tvb, offset, 1, FALSE);
++ proto_tree_add_uint_format(gsmtap_tree, hf_gsmtap_hdrlen,
++ tvb, offset+1, 1, hdr_len,
++ "Header length: %u bytes", hdr_len);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_type,
++ tvb, offset+2, 1, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_timeslot,
++ tvb, offset+3, 1, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_arfcn,
++ tvb, offset+4, 2, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_uplink,
++ tvb, offset+4, 2, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_noise_dbm,
++ tvb, offset+6, 1, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_signal_dbm,
++ tvb, offset+7, 1, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_frame_nr,
++ tvb, offset+8, 4, FALSE);
++ if (type == GSMTAP_TYPE_UM_BURST)
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_burst_type,
++ tvb, offset+12, 1, FALSE);
++ else if (type == GSMTAP_TYPE_UM)
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_channel_type,
++ tvb, offset+12, 1, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_antenna,
++ tvb, offset+13, 1, FALSE);
++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_subslot,
++ tvb, offset+14, 1, FALSE);
++ }
++
++ switch (type) {
++ case GSMTAP_TYPE_UM:
++ if (l1h_tvb)
++ dissect_sacch_l1h(l1h_tvb, tree);
++ switch (sub_type & ~GSMTAP_CHANNEL_ACCH) {
++ case GSMTAP_CHANNEL_BCCH:
++ case GSMTAP_CHANNEL_CCCH:
++ case GSMTAP_CHANNEL_PCH:
++ case GSMTAP_CHANNEL_AGCH:
++ /* FIXME: we might want to skip idle frames */
++ sub_handle = GSMTAP_SUB_UM;
++ break;
++ case GSMTAP_CHANNEL_SDCCH:
++ case GSMTAP_CHANNEL_SDCCH4:
++ case GSMTAP_CHANNEL_SDCCH8:
++ case GSMTAP_CHANNEL_TCH_F:
++ case GSMTAP_CHANNEL_TCH_H:
++ sub_handle = GSMTAP_SUB_UM_LAPDM;
++ break;
++ case GSMTAP_CHANNEL_RACH:
++ default:
++ sub_handle = GSMTAP_SUB_DATA;
++ break;
++ }
++ break;
++ case GSMTAP_TYPE_UM_BURST:
++ default:
++ sub_handle = GSMTAP_SUB_DATA;
++ break;
++ }
++ call_dissector(sub_handles[sub_handle], payload_tvb, pinfo, tree);
++}
++
++static const true_false_string sacch_l1h_fpc_mode_vals = {
++ "In use",
++ "Not in use"
++};
++
++void
++proto_register_gsmtap(void)
++{
++ static hf_register_info hf[] = {
++ { &hf_gsmtap_version, { "Version", "gsmtap.version",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_hdrlen, { "Header Length", "gsmtap.hdr_len",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_type, { "Payload Type", "gsmtap.type",
++ FT_UINT8, BASE_DEC, VALS(gsmtap_types), 0, NULL, HFILL } },
++ { &hf_gsmtap_timeslot, { "Time Slot", "gsmtap.ts",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_arfcn, { "ARFCN", "gsmtap.arfcn",
++ FT_UINT16, BASE_DEC, NULL, GSMTAP_ARFCN_MASK, NULL, HFILL } },
++ { &hf_gsmtap_uplink, { "Uplink", "gsmtap.uplink",
++ FT_UINT16, BASE_DEC, NULL, GSMTAP_ARFCN_F_UPLINK, NULL, HFILL } },
++ { &hf_gsmtap_noise_dbm, { "Signal/Noise Ratio (dB)", "gsmtap.snr_db",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_signal_dbm, { "Signal Level (dBm)", "gsmtap.signal_dbm",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_frame_nr, { "GSM Frame Number", "gsmtap.frame_nr",
++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_burst_type, { "Burst Type", "gsmtap.burst_type",
++ FT_UINT8, BASE_DEC, VALS(gsmtap_bursts), 0, NULL, HFILL }},
++ { &hf_gsmtap_channel_type, { "Channel Type", "gsmtap.chan_type",
++ FT_UINT8, BASE_DEC, VALS(gsmtap_channels), 0, NULL, HFILL }},
++ { &hf_gsmtap_antenna, { "Antenna Number", "gsmtap.antenna",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ { &hf_gsmtap_subslot, { "Sub-Slot", "gsmtap.sub_slot",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++
++ { &hf_sacch_l1h_power_lev, { "MS power level", "gsmtap.sacch_l1.power_lev",
++ FT_UINT8, BASE_DEC, NULL, 0x1f, NULL, HFILL } },
++ { &hf_sacch_l1h_fpc, { "FPC", "gsmtap.sacch_l1.fpc",
++ FT_BOOLEAN, 8, TFS(&sacch_l1h_fpc_mode_vals), 0x04,
++ NULL, HFILL } },
++ { &hf_sacch_l1h_ta, { "Actual Timing Advance", "gsmtap.sacch_l1.ta",
++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
++ };
++ static gint *ett[] = {
++ &ett_gsmtap
++ };
++
++ proto_gsmtap = proto_register_protocol("GSM Radiotap", "GSMTAP", "gsmtap");
++ proto_register_field_array(proto_gsmtap, hf, array_length(hf));
++ proto_register_subtree_array(ett, array_length(ett));
++}
++
++void
++proto_reg_handoff_gsmtap(void)
++{
++ dissector_handle_t gsmtap_handle;
++
++ sub_handles[GSMTAP_SUB_DATA] = find_dissector("data");
++ sub_handles[GSMTAP_SUB_UM] = find_dissector("gsm_a_ccch");
++ sub_handles[GSMTAP_SUB_UM_LAPDM] = find_dissector("lapdm");
++ sub_handles[GSMTAP_SUB_ABIS] = find_dissector("gsm_a_dtap");
++ gsmtap_handle = create_dissector_handle(dissect_gsmtap, proto_gsmtap);
++ dissector_add("udp.port", GSMTAP_UDP_PORT, gsmtap_handle);
++}
+Index: epan/dissectors/packet-gsmtap.h
+===================================================================
+--- /dev/null
++++ epan/dissectors/packet-gsmtap.h
+@@ -0,0 +1,70 @@
++#ifndef _GSMTAP_H
++#define _GSMTAP_H
++
++/* gsmtap header, pseudo-header in front of the actua/ GSM payload */
++
++/* GSMTAP is a generic header format for GSM protocol captures,
++ * it uses the IANA-assigned UDP port number 4729 and carries
++ * payload in various formats of GSM interfaces such as Um MAC
++ * blocks or Um bursts.
++ *
++ * Example programs generating GSMTAP data are airprobe
++ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/)
++ */
++
++#define GSMTAP_TYPE_UM 0x01
++#define GSMTAP_TYPE_ABIS 0x02
++#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */
++
++#define GSMTAP_BURST_UNKNOWN 0x00
++#define GSMTAP_BURST_FCCH 0x01
++#define GSMTAP_BURST_PARTIAL_SCH 0x02
++#define GSMTAP_BURST_SCH 0x03
++#define GSMTAP_BURST_CTS_SCH 0x04
++#define GSMTAP_BURST_COMPACT_SCH 0x05
++#define GSMTAP_BURST_NORMAL 0x06
++#define GSMTAP_BURST_DUMMY 0x07
++#define GSMTAP_BURST_ACCESS 0x08
++#define GSMTAP_BURST_NONE 0x09
++
++#define GSMTAP_CHANNEL_UNKNOWN 0x00
++#define GSMTAP_CHANNEL_BCCH 0x01
++#define GSMTAP_CHANNEL_CCCH 0x02
++#define GSMTAP_CHANNEL_RACH 0x03
++#define GSMTAP_CHANNEL_AGCH 0x04
++#define GSMTAP_CHANNEL_PCH 0x05
++#define GSMTAP_CHANNEL_SDCCH 0x06
++#define GSMTAP_CHANNEL_SDCCH4 0x07
++#define GSMTAP_CHANNEL_SDCCH8 0x08
++#define GSMTAP_CHANNEL_TCH_F 0x09
++#define GSMTAP_CHANNEL_TCH_H 0x0a
++#define GSMTAP_CHANNEL_ACCH 0x80
++
++#define GSMTAP_ARFCN_F_PCS 0x8000
++#define GSMTAP_ARFCN_F_UPLINK 0x4000
++#define GSMTAP_ARFCN_MASK 0x3fff
++
++#define GSMTAP_UDP_PORT 4729
++
++/* This is the header as it is used by gsmtap-generating software.
++ * It is not used by the wireshark dissector and provided for reference only.
++struct gsmtap_hdr {
++ guint8 version; // version, set to 0x01 currently
++ guint8 hdr_len; // length in number of 32bit words
++ guint8 type; // see GSMTAP_TYPE_*
++ guint8 timeslot; // timeslot (0..7 on Um)
++
++ guint16 arfcn; // ARFCN (frequency)
++ gint8 signal_dbm; // signal level in dBm
++ gint8 snr_db; // signal/noise ratio in dB
++
++ guint32 frame_number; // GSM Frame Number (FN)
++
++ guint8 sub_type; // Type of burst/channel, see above
++ guint8 antenna_nr; // Antenna Number
++ guint8 sub_slot; // sub-slot within timeslot
++ guint8 res; // reserved for future use (RFU)
++}
++ */
++
++#endif /* _GSMTAP_H */
+Index: epan/dissectors/Makefile.common
+===================================================================
+--- epan/dissectors/Makefile.common.orig
++++ epan/dissectors/Makefile.common
+@@ -484,6 +484,7 @@
+ packet-gsm_sms.c \
+ packet-gsm_sms_ud.c \
+ packet-gsm_um.c \
++ packet-gsmtap.c \
+ packet-gssapi.c \
+ packet-gtp.c \
+ packet-gtpv2.c \
+@@ -1072,6 +1073,7 @@
+ packet-gsm_a_common.h \
+ packet-gsm_map.h \
+ packet-gsm_sms.h \
++ packet-gsmtap.h \
+ packet-gssapi.h \
+ packet-gtp.h \
+ packet-h223.h \